Validate array object - Swagger/NestJS - javascript

I am wondering if there's a way to create a dto to validate array of object?
Example array:
[
{
"name": "Tag 1",
"description": "This is the first tag"
},
{
"name": "Tag 2",
"description": "This is the second tag"
}
]
At the moment I have this, while it works, it isn't what I am after.
export class Tags {
#ApiProperty({
description: 'The name of the tag',
example: 'Tag 1',
required: true
})
#IsString()
#MaxLength(30)
#MinLength(1)
name: string;
#ApiProperty({
description: 'The description of the tag',
example: 'This is the first tag',
required: true
})
#IsString()
#MinLength(3)
description: string;
}
export class CreateTagDto {
#ApiProperty({ type: [Tags] })
#Type(() => Tags)
#ArrayMinSize(1)
#ValidateNested({ each: true })
tags: Tags[];
}

Just use ParseArrayPipe:
Update your Controller:
#Post()
createExample(#Body(new ParseArrayPipe({ items: Tags, whitelist: true })) body: Tags[]) {
...
}
Ensure to have items and whitelist set.
Update your DTO:
import { IsString, Length } from "class-validator";
export class Tags {
#IsString()
#Length(1, 30)
name: string;
#IsString()
#Length(3)
description: string;
}

Related

Unable to validate nested dto class in another class in nest js

I am trying to access 2 classes in one dto class but when I check the schema in swagger, it shows only string instead of complete schema. I have gone through a lot of stackoverflow posts but i couldn't find any issue with my dto schema. Please help me in correcting the dto.
This is the schema visible in swagger
{
"originDestinations": [
"string"
],
"travelers": [
"string"
]
}
This is what I want.
{
"originDestinations": [
{
"originLocationCode": "string",
"destinationLocationCode": "string",
"departureDate": "string"
}
],
"travelers": [
{
"travelerType": "string",
"fareOptions": [
"string"
]
}
]
}
When I am running it in postman I am getting an error response where I am getting validations in message.
This is my dto
import { ApiProperty, ApiPropertyOptional } from "#nestjs/swagger";
import { Type } from "class-transformer";
import { IsNotEmpty, IsArray, ValidateNested, IsDateString, MinLength, MaxLength, } from "class-validator";
class Travelers{
#ApiProperty({ enum: [ "ADULT", "CHILD", "SENIOR", "YOUNG", "HELD_INFANT", "SEATED_INFANT", "STUDENT" ]})
travelerType:string;
#ApiProperty({ enum: [ "STANDARD", "INCLUSIVE_TOUR", "SPANISH_MELILLA_RESIDENT", "SPANISH_CEUTA_RESIDENT", "SPANISH_CANARY_RESIDENT", "SPANISH_BALEARIC_RESIDENT", "AIR_FRANCE_METROPOLITAN_DISCOUNT_PASS", "AIR_FRANCE_DOM_DISCOUNT_PASS", "AIR_FRANCE_COMBINED_DISCOUNT_PASS", "AIR_FRANCE_FAMILY", "ADULT_WITH_COMPANION", "COMPANION" ]})
fareOptions: string[];
}
class OriginDestinations {
#ApiProperty()
#IsNotEmpty()
#MinLength(3)
#MaxLength(3)
originLocationCode:string;
#ApiProperty()
#IsNotEmpty()
#MinLength(3)
#MaxLength(3)
destinationLocationCode:string;
#ApiProperty()
#IsDateString()
#IsNotEmpty()
departureDate:string;
// #ApiProperty()
// #IsNotEmpty()
// adults:string;
}
export class originDestinationsDto{
#IsArray()
#ApiProperty()
#IsNotEmpty()
#ValidateNested({ each: true })
#Type(() => OriginDestinations)
originDestinations:OriginDestinations[];
#ApiProperty()
#IsArray()
#IsNotEmpty()
#ValidateNested({ each: true })
#Type(() => Travelers)
travelers:Travelers[];
}
If I try to hit it with postman this is the response generate there
{
"statusCode": 400,
"message": [
"originLocationCode must be shorter than or equal to 3 characters",
"originLocationCode must be longer than or equal to 3 characters",
"originLocationCode should not be empty",
"destinationLocationCode must be shorter than or equal to 3 characters",
"destinationLocationCode must be longer than or equal to 3 characters",
"destinationLocationCode should not be empty",
"departureDate should not be empty",
"departureDate must be a valid ISO 8601 date string"
],
"error": "Bad Request"
}
Swagger cant automatically recognise, your params as objects, so you need to specify it by setting type param:
export class originDestinationsDto{
#ApiProperty({ type: [OriginDestinations] })
originDestinations:OriginDestinations[];
#ApiProperty({ type: [Travelers] })
travelers:Travelers[];
}
take a note, for array, type should be array with specific DTO { type: [Travelers] } while for single object it should be { type: Travelers }

How to initialize typeorm entity by considering its nullable columns

I would like to set value to typeorm entity and insert them to DB.
import { PricingPatternElement } from file
const Element:PricingPatternElement = {
displayOrder : 10,
elementName : "test",
createdAt : getCurrentDate(),
createdBy : "test"
}
When I set above value to PricingPatternElement it returned following error.
Type '{ displayOrder: number; elementName: string; createdAt: Date; createdBy: string; }' is missing the following properties from type 'PricingPatternElement': pricingPatternElementId, minPrice, maxPrice, priceInterval, and 15 more.
it shows 15 members are not set . but actually, I set nullable as follows
so that I need not set nullable values.
my desired goal is to set value avoiding to set nullable columns.
I must set only 5 columnsaccording to its entity definitions.
import {
BaseEntity,
Column,
Entity,
Index,
JoinColumn,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from "typeorm";
import { PricingPattern } from "./pricingPattern";
import { InputPricingElement } from "./inputPricingElement";
#Index("pricing_pattern_element_pkc", ["pricingPatternElementId"], {
unique: true,
})
#Entity("pricing_pattern_element", { schema: "atai" })
export class PricingPatternElement extends BaseEntity {
#PrimaryGeneratedColumn({
type: "bigint",
name: "pricing_pattern_element_id",
})
pricingPatternElementId: string;
#Column("integer", { name: "display_order" })
displayOrder: number;
#Column("text", { name: "element_name" })
elementName: string;
#Column("integer", { name: "min_price", nullable: true })
minPrice: number | null;
#Column("integer", { name: "max_price", nullable: true })
maxPrice: number | null;
#Column("integer", { name: "price_interval", nullable: true })
priceInterval: number | null;
#Column("character varying", {
name: "icon_image",
nullable: true,
length: 256,
})
iconImage: string | null;
#Column("text", { name: "additional_explanation", nullable: true })
additionalExplanation: string | null;
#Column("integer", { name: "default_element_price", nullable: true })
defaultElementPrice: number | null;
#Column("timestamp without time zone", { name: "created_at" })
createdAt: Date;
#Column("character varying", { name: "created_by", length: 256 })
createdBy: string;
#Column("timestamp without time zone", { name: "updated_at", nullable: true })
updatedAt: Date | null;
#Column("character varying", {
name: "updated_by",
nullable: true,
length: 256,
})
updatedBy: string | null;
#Column("timestamp without time zone", { name: "revoked_at", nullable: true })
revokedAt: Date | null;
#Column("character varying", {
name: "revoked_by",
nullable: true,
length: 256,
})
revokedBy: string | null;
#ManyToOne(
() => PricingPattern,
(pricingPattern) => pricingPattern.pricingPatternElements
)
#JoinColumn([
{ name: "pricing_pattern_id", referencedColumnName: "pricingPatternId" },
])
pricingPattern: PricingPattern;
#OneToMany(
() => InputPricingElement,
(inputPricingElement) => inputPricingElement.pricingPatternElement
)
inputPricingElements: InputPricingElement[];
}
How can I set value avoiding to set nullable columns ?
Thanks
Well, one of the way is to use "as":
const Element = {
displayOrder : 10,
elementName : "test",
createdAt : getCurrentDate(),
createdBy : "test"
} as PricingPatternElement;
But you should understand that if there will be default params it will not exists in this object (but it could be used in tests, for example)
Probably you trying to find https://orkhan.gitbook.io/typeorm/docs/repository-api:
const user = repository.create({
id: 1,
firstName: "Timber",
lastName: "Saw",
});
This will create an entity without inserting it into db

How can I describe type indexing using it's own keys in TypeScript?

I'm new to TypeScript, sorry if my question is dumb.
Is there a way to point out the indexing for this type so that it use it's own keys instead of just an object?
export type TypeAbCreationModal = {
[index: string]: object | TypeExperience;
name: {
title: string;
placeholder: string;
};
server: {
title: string;
tooltip: string;
};
site: {
title: string;
placeholder: string;
placeholderSearch: string;
};
};
Edit:
Object typed with TypeAbCreationModal looks like this :
const myObj = {
name: {
title: 'Some Title',
placeholder: 'Some Placeholder',
},
server: {
title: 'Some Title',
tooltip: 'Some Tooltip',
},
site: {
title: 'Some Title',
placeholder: 'Some Placeholder',
placeholderSearch: 'Some Placeholder',
},
}
At some point I'll have to access it like this
myObj[someIndex].title
(someIndex is of the TypeExperience type)
If I use an object as an index TS throws an error "title can not assign to object"
[index: string]: object | TypeExperience; means TypeAbCreationModal can have any string key whose potential values are either TypeExperience or a primitive js object type, which don't have the properties you're trying to access. You probably want something like this instead of a primitive js object type:
interface MyObject {
title: string,
placeholder?: string,
placeholderSearch?: string,
tooltip?: string
}
Then update TypeAbCreationModal to use your new type, you don't need TypeExperience here:
[index: string]: MyObject;
BTW: I would change TypeAbCreationModal to not use type but rather interface (the type construct is usually used for creating union types or type aliases):
export interface TypeAbCreationModal {
[index: string]: MyObject;
}
Note: The above setup will allow TypeAbCreationModal objects to have any string key so long as the value is of type MyObject, of which has optional properties. It's possible you don't want this but rather very specifically for it to only have a name, server, and site properties which can all have strict
types of their own. This gives you more type safety (although much more verbose):
interface MyBaseObj {
title: string
}
interface MyNameObj extends MyBaseObj {
placeholder: string;
}
interface MyServerObj extends MyBaseObj {
tooltip: string;
}
interface MySiteObj extends MyNameObj {
placeholderSearch: string;
}
interface TypeAbCreationModal {
name: MyNameObj,
server: MyServerObj,
site: MySiteObj
}
const myObj: TypeAbCreationModal = {
name: {
title: 'Some Title',
placeholder: 'Some Placeholder',
},
server: {
title: 'Some Title',
tooltip: 'Some Tooltip',
},
site: {
title: 'Some Title',
placeholder: 'Some Placeholder',
placeholderSearch: 'Some Placeholder',
},
}
console.log(myObj["name"].title);
console.log(myObj["server"].tooltip);
console.log(myObj["site"].placeholder);

how to define location in an entity using mongodb

I am using nest js in my sample application. I define the entity of my application .Entity means a document. But I struck at one place to define the location.
using mongoose I define the schema of my document like this see link
https://raw.githubusercontent.com/webmakaka/Node.js-API-Masterclass-With-Express-MongoDB/master/api/models/Bootcamp.js
location: {
// GeoJSON Point
type: {
type: String,
enum: ['Point']
},
coordinates: {
type: [Number],
index: '2dsphere'
},
formattedAddress: String,
street: String,
city: String,
state: String,
zipcode: String,
country: String
},
careers: {
// Array of strings
type: [String],
required: true,
enum: [
'Web Development',
'Mobile Development',
'UI/UX',
'Data Science',
'Business',
'Other'
]
},
same thing I want to do using typeorm using mongoDB without mongoos .can you please help me how I will do that.
here is my entity class
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, ManyToOne, ObjectID, ObjectIdColumn } from 'typeorm';
import { IsNotEmpty, MaxLength } from 'class-validator';
#Entity('bootcamp')
export class BootcampEntity extends BaseEntity {
#ObjectIdColumn() id: ObjectID;
#Column({type:'text',unique:true,length:50,nullable: false})
name:string;
#Column({type:'text'})
slug: string;
#Column({type:'text',length:500,nullable: false})
description: string;
#Column({type:'text'})
website: string;
#Column({type:'text',length:20})
phone: string;
#Column({type:'text',})
email: string;
#Column({type:'text',nullable: false})
address: string;
#Column({type:'text',array: true })
careers: string[];
#Column({type:'int'})
averageRating:number
#Column({type:'int'})
averageCost:number
//
#Column({type:'string',default:'no-photo.jpg'})
photo: string;
//
#Column({type:'boolean',default:false})
housing: boolean;
#Column({type:'boolean',default:false})
jobAssistance: boolean;
#Column({type:'boolean',default:false})
jobGuarantee: boolean;
#Column({type:'boolean',default:false})
acceptGi: boolean;
#Column({type:'date',default:Date.now()})
createdAt: Date;
}
I am using this framework
https://docs.nestjs.com/
typeorm link
https://typeorm.io/#/entities
there is schema
https://raw.githubusercontent.com/webmakaka/Node.js-API-Masterclass-With-Express-MongoDB/master/api/models/Bootcamp.js
I want make same schema using typeorm .I struck only defining the location attribute ?
how to define ?could you please tell me how I will define location in my entity class ?
I think location would have similar structure to code below.
source 1
source 2
enum GeoJSONPoint {
Point = "Point"
}
enum Careers {
WebDevelopment = 'Web Development',
MobileDevelopment = 'Mobile Development',
UIUX = 'UI/UX',
DataScience = 'Data Science',
Business = 'Business',
Other = 'Other'
}
#Entity('location')
export class LocationEntity extends BaseEntity {
#Column({
type: "enum",
enum: GeoJSONPoint
})
type: GeoJSONPoint;
#Column({type:'int'})
coordinates: number;
#Column({type:'text'})
formattedAddress: string;
#Column({type:'text'})
street: string;
#Column({type:'text'})
city: string;
#Column({type:'text'})
state: string;
#Column({type:'text'})
zipcode: string;
#Column({type:'text'})
country: string;
#Column({type:'simple-array'})
careers: Careers[];
}

Can't update relation n:m

I have two entities with relation n:m
#ObjectType()
#Entity("tags")
export class TagEntity extends BaseEntity {
#Field(() => ID)
#PrimaryGeneratedColumn()
id: number;
#Field(() => [PostEntity])
#ManyToMany(() => PostEntity, post => post.tags)
posts: PostEntity[];
}
#ObjectType()
#Entity("posts")
export class PostEntity extends BaseEntity {
#Field(() => ID)
#PrimaryGeneratedColumn()
id: number;
#Field(() => [TagEntity])
#ManyToMany(() => TagEntity, tag => tag.posts)
#JoinTable()
tags: TagEntity[];
}
When I try to update post with new tag emit error:
"column "postsId" of relation "posts" does not exist"
Update logic:
await this.postsRepository.update({ id }, postData);
postData:
{ id: '1',
title: 'new post title',
url: 'new post url',
tags:
[ TagEntity {
id: 2,
title: 'tag title 2',
url: 'tag url 2',
createdAt: 2019-03-04T08:56:50.531Z,
updatedAt: 2019-03-04T08:56:50.531Z } ] }
Generated sql:
UPDATE "posts" SET "id" = $2, "title" = $3, "url" = $4, "postsId" = $5, "updatedAt" = CURRENT_TIMESTAMP WHERE "id" = $1 -- PARAMETERS: ["1","1","new post title","new post url",null]

Categories

Resources