diff --git a/CHANGELOG.md b/CHANGELOG.md index f7ee958ee..179af61e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,5 @@ # Changelog and release notes - ### 0.2.3 [BREAKING CHANGE] #### Changed @@ -24,97 +23,97 @@ This version has introduced a breaking-change when this library is used with cla #### Added -- add option to strip unkown properties via using the `excludeExtraneousValues` option +- add option to strip unkown properties via using the `excludeExtraneousValues` option ### 0.2.0 [BREAKING CHANGE] #### Added -- add documentation for using `Set`s and `Map`s -- add opotion to pass a discriminator function to convert values into different types based on custom conditions -- added support for polymorphism based on a named type property +- add documentation for using `Set`s and `Map`s +- add opotion to pass a discriminator function to convert values into different types based on custom conditions +- added support for polymorphism based on a named type property #### Fixed -- fix bug when transforming `null` values as primitives +- fix bug when transforming `null` values as primitives ### 0.1.10 #### Fixed -- improve MetadataStorage perf by changing from Arrays to ES6 Maps by @sheiidan -- fixed getAncestor issue with unknown nested properties by @247GradLabs +- improve MetadataStorage perf by changing from Arrays to ES6 Maps by @sheiidan +- fixed getAncestor issue with unknown nested properties by @247GradLabs ### 0.1.9 #### Fixed -- objects with `null` prototype are converted properly now -- objects with unknown non primitive properties are converted properly now -- corrected a typo in the README.md -- fixed the deserialize example in the README.md +- objects with `null` prototype are converted properly now +- objects with unknown non primitive properties are converted properly now +- corrected a typo in the README.md +- fixed the deserialize example in the README.md ### 0.1.4 #### Added -- added `TransformClassToPlain` and `TransformClassToClass` decorators +- added `TransformClassToPlain` and `TransformClassToClass` decorators ### 0.1.0 #### Added -- renamed library from `constructor-utils` to `class-transformer` -- completely renamed most of names -- renamed all main methods: `plainToConstructor` now is `plainToClass` and `constructorToPlain` is `classToPlain`, etc. -- `plainToConstructorArray` method removed - now `plainToClass` handles it -- `@Skip()` decorator renamed to `@Exclude()` -- added `@Expose` decorator -- added lot of new options: groups, versioning, custom names, etc. -- methods and getters that should be exposed must be decorated with `@Expose` decorator -- added `excludedPrefix` to class transform options that allows exclude properties that start with one of the given prefix +- renamed library from `constructor-utils` to `class-transformer` +- completely renamed most of names +- renamed all main methods: `plainToConstructor` now is `plainToClass` and `constructorToPlain` is `classToPlain`, etc. +- `plainToConstructorArray` method removed - now `plainToClass` handles it +- `@Skip()` decorator renamed to `@Exclude()` +- added `@Expose` decorator +- added lot of new options: groups, versioning, custom names, etc. +- methods and getters that should be exposed must be decorated with `@Expose` decorator +- added `excludedPrefix` to class transform options that allows exclude properties that start with one of the given prefix ### 0.0.22 #### Fixed -- fixed array with primitive types being converted +- fixed array with primitive types being converted ### 0.0.18-0.0.21 #### Fixed -- fixed bugs when getters are not converted with es6 target +- fixed bugs when getters are not converted with es6 target ### 0.0.17 #### Fixed -- fixed issue #4 -- added type guessing during transformation from constructor to plain object -- added sample with generics +- fixed issue #4 +- added type guessing during transformation from constructor to plain object +- added sample with generics ### 0.0.16 #### Changed -- renamed `constructor-utils/constructor-utils` to `constructor-utils` package namespace +- renamed `constructor-utils/constructor-utils` to `constructor-utils` package namespace ### 0.0.15 #### Removed -- removed code mappings from package +- removed code mappings from package ### 0.0.14 #### Removed -- removed `import "reflect-metadata"` from source code. Now reflect metadata should be included like any other shims. +- removed `import "reflect-metadata"` from source code. Now reflect metadata should be included like any other shims. ### 0.0.13 #### Changed -- Library has changed its name from `serializer.ts` to `constructor-utils`. -- Added `constructor-utils` namespace. +- Library has changed its name from `serializer.ts` to `constructor-utils`. +- Added `constructor-utils` namespace. diff --git a/sample/sample1-simple-usage/Album.ts b/sample/sample1-simple-usage/Album.ts index 46f190f57..7de2e2376 100644 --- a/sample/sample1-simple-usage/Album.ts +++ b/sample/sample1-simple-usage/Album.ts @@ -1,14 +1,12 @@ -import {Type, Exclude} from "../../src/decorators"; -import {Photo} from "./Photo"; +import { Type, Exclude } from '../../src/decorators'; +import { Photo } from './Photo'; export class Album { + id: string; - id: string; + @Exclude() + name: string; - @Exclude() - name: string; - - @Type(() => Photo) - photos: Photo[]; - -} \ No newline at end of file + @Type(() => Photo) + photos: Photo[]; +} diff --git a/sample/sample1-simple-usage/Photo.ts b/sample/sample1-simple-usage/Photo.ts index 94167cd60..20ac359d7 100644 --- a/sample/sample1-simple-usage/Photo.ts +++ b/sample/sample1-simple-usage/Photo.ts @@ -1,30 +1,28 @@ -import {Type} from "../../src/decorators"; -import {Album} from "./Album"; -import {User} from "./User"; +import { Type } from '../../src/decorators'; +import { Album } from './Album'; +import { User } from './User'; export class Photo { + id: string; - id: string; + filename: string; - filename: string; + description: string; - description: string; + tags: string[]; - tags: string[]; + @Type(() => User) + author: User; - @Type(() => User) - author: User; + @Type(() => Album) + albums: Album[]; - @Type(() => Album) - albums: Album[]; - - get name() { - return this.id + "_" + this.filename; - } + get name() { + return this.id + '_' + this.filename; + } - getAlbums() { - console.log("this is not serialized/deserialized"); - return this.albums; - } - -} \ No newline at end of file + getAlbums() { + console.log('this is not serialized/deserialized'); + return this.albums; + } +} diff --git a/sample/sample1-simple-usage/User.ts b/sample/sample1-simple-usage/User.ts index 0c2dc9e5b..37c1e38be 100644 --- a/sample/sample1-simple-usage/User.ts +++ b/sample/sample1-simple-usage/User.ts @@ -1,15 +1,13 @@ -import {Type} from "../../src/decorators"; +import { Type } from '../../src/decorators'; export class User { + @Type(() => Number) + id: number; - @Type(() => Number) - id: number; - - firstName: string; - - lastName: string; - - @Type(() => Date) - registrationDate: Date; - -} \ No newline at end of file + firstName: string; + + lastName: string; + + @Type(() => Date) + registrationDate: Date; +} diff --git a/sample/sample1-simple-usage/app.ts b/sample/sample1-simple-usage/app.ts index 425cc9258..a1df30698 100644 --- a/sample/sample1-simple-usage/app.ts +++ b/sample/sample1-simple-usage/app.ts @@ -1,86 +1,91 @@ -import "es6-shim"; -import "reflect-metadata"; -import {plainToClass, classToPlain} from "../../src/index"; -import {Photo} from "./Photo"; +import 'es6-shim'; +import 'reflect-metadata'; +import { plainToClass, classToPlain } from '../../src/index'; +import { Photo } from './Photo'; // check deserialization let photoJson = { - id: "1", - filename: "myphoto.jpg", - description: "about my photo", - tags: [ - "me", - "iam" - ], - author: { - id: "2", - firstName: "Johny", - lastName: "Cage" - }, - albums: [{ - id: "1", - name: "My life" + id: '1', + filename: 'myphoto.jpg', + description: 'about my photo', + tags: ['me', 'iam'], + author: { + id: '2', + firstName: 'Johny', + lastName: 'Cage', + }, + albums: [ + { + id: '1', + name: 'My life', }, { - id: "2", - name: "My young years" - }] + id: '2', + name: 'My young years', + }, + ], }; let photo = plainToClass(Photo, photoJson); -console.log("deserialized object: " , photo); +console.log('deserialized object: ', photo); // now check serialization let newPhotoJson = classToPlain(photo); -console.log("serialized object: " , newPhotoJson); +console.log('serialized object: ', newPhotoJson); // try to deserialize an array -console.log("-------------------------------"); +console.log('-------------------------------'); -let photosJson = [{ - id: "1", - filename: "myphoto.jpg", - description: "about my photo", +let photosJson = [ + { + id: '1', + filename: 'myphoto.jpg', + description: 'about my photo', author: { - id: "2", - firstName: "Johny", - lastName: "Cage", - registrationDate: "1995-12-17T03:24:00" - }, - albums: [{ - id: "1", - name: "My life" + id: '2', + firstName: 'Johny', + lastName: 'Cage', + registrationDate: '1995-12-17T03:24:00', }, - { - id: "2", - name: "My young years" - }] -}, -{ - id: "2", - filename: "hisphoto.jpg", - description: "about his photo", + albums: [ + { + id: '1', + name: 'My life', + }, + { + id: '2', + name: 'My young years', + }, + ], + }, + { + id: '2', + filename: 'hisphoto.jpg', + description: 'about his photo', author: { - id: "2", - firstName: "Johny", - lastName: "Cage" - }, - albums: [{ - id: "1", - name: "My life" + id: '2', + firstName: 'Johny', + lastName: 'Cage', }, - { - id: "2", - name: "My young years" - }] -}]; + albums: [ + { + id: '1', + name: 'My life', + }, + { + id: '2', + name: 'My young years', + }, + ], + }, +]; let photos = plainToClass(Photo, photosJson); -console.log("deserialized array: " , photos); +console.log('deserialized array: ', photos); // now check array serialization let newPhotosJson = classToPlain(photos); -console.log("serialized array: " , newPhotosJson); \ No newline at end of file +console.log('serialized array: ', newPhotosJson); diff --git a/sample/sample2-iheritance/Album.ts b/sample/sample2-iheritance/Album.ts index f3b284bf8..a5db8c867 100644 --- a/sample/sample2-iheritance/Album.ts +++ b/sample/sample2-iheritance/Album.ts @@ -1,15 +1,13 @@ -import {Type, Exclude} from "../../src/decorators"; -import {Photo} from "./Photo"; -import {Authorable} from "./Authorable"; +import { Type, Exclude } from '../../src/decorators'; +import { Photo } from './Photo'; +import { Authorable } from './Authorable'; export class Album extends Authorable { + id: string; - id: string; + @Exclude() + name: string; - @Exclude() - name: string; - - @Type(() => Photo) - photos: Photo[]; - -} \ No newline at end of file + @Type(() => Photo) + photos: Photo[]; +} diff --git a/sample/sample2-iheritance/Authorable.ts b/sample/sample2-iheritance/Authorable.ts index e9028d8e3..0c04bc1e9 100644 --- a/sample/sample2-iheritance/Authorable.ts +++ b/sample/sample2-iheritance/Authorable.ts @@ -1,14 +1,12 @@ -import {Type, Exclude} from "../../src/decorators"; -import {User} from "./User"; +import { Type, Exclude } from '../../src/decorators'; +import { User } from './User'; export class Authorable { - - authorName: string; + authorName: string; - @Exclude() - authorEmail: string; - - @Type(() => User) - author: User; + @Exclude() + authorEmail: string; -} \ No newline at end of file + @Type(() => User) + author: User; +} diff --git a/sample/sample2-iheritance/Photo.ts b/sample/sample2-iheritance/Photo.ts index 493a37589..3baf2973a 100644 --- a/sample/sample2-iheritance/Photo.ts +++ b/sample/sample2-iheritance/Photo.ts @@ -1,19 +1,17 @@ -import {Type, Exclude} from "../../src/decorators"; -import {Album} from "./Album"; -import {Authorable} from "./Authorable"; +import { Type, Exclude } from '../../src/decorators'; +import { Album } from './Album'; +import { Authorable } from './Authorable'; export class Photo extends Authorable { + id: string; - id: string; + filename: string; - filename: string; + description: string; - description: string; + @Exclude() // this will ignore skipping inherited from Authorable class + authorEmail: string; - @Exclude() // this will ignore skipping inherited from Authorable class - authorEmail: string; - - @Type(() => Album) - albums: Album[]; - -} \ No newline at end of file + @Type(() => Album) + albums: Album[]; +} diff --git a/sample/sample2-iheritance/User.ts b/sample/sample2-iheritance/User.ts index 0c2dc9e5b..37c1e38be 100644 --- a/sample/sample2-iheritance/User.ts +++ b/sample/sample2-iheritance/User.ts @@ -1,15 +1,13 @@ -import {Type} from "../../src/decorators"; +import { Type } from '../../src/decorators'; export class User { + @Type(() => Number) + id: number; - @Type(() => Number) - id: number; - - firstName: string; - - lastName: string; - - @Type(() => Date) - registrationDate: Date; - -} \ No newline at end of file + firstName: string; + + lastName: string; + + @Type(() => Date) + registrationDate: Date; +} diff --git a/sample/sample2-iheritance/app.ts b/sample/sample2-iheritance/app.ts index d99d2931c..86cbdc454 100644 --- a/sample/sample2-iheritance/app.ts +++ b/sample/sample2-iheritance/app.ts @@ -1,86 +1,94 @@ -import "es6-shim"; -import "reflect-metadata"; -import {classToPlain, plainToClass} from "../../src/index"; -import {Photo} from "./Photo"; +import 'es6-shim'; +import 'reflect-metadata'; +import { classToPlain, plainToClass } from '../../src/index'; +import { Photo } from './Photo'; let photoJson = { - id: "1", - filename: "myphoto.jpg", - description: "about my photo", - authorName: "Johny.Cage", - authorEmail: "johny@cage.com", - author: { - id: "2", - firstName: "Johny", - lastName: "Cage" - }, - albums: [{ - id: "1", - authorName: "Johny.Cage", - authorEmail: "johny@cage.com", - name: "My life" + id: '1', + filename: 'myphoto.jpg', + description: 'about my photo', + authorName: 'Johny.Cage', + authorEmail: 'johny@cage.com', + author: { + id: '2', + firstName: 'Johny', + lastName: 'Cage', + }, + albums: [ + { + id: '1', + authorName: 'Johny.Cage', + authorEmail: 'johny@cage.com', + name: 'My life', }, { - id: "2", - authorName: "Johny.Cage", - authorEmail: "johny@cage.com", - name: "My young years" - }] + id: '2', + authorName: 'Johny.Cage', + authorEmail: 'johny@cage.com', + name: 'My young years', + }, + ], }; let photo = plainToClass(Photo, photoJson); -console.log("deserialized object: " , photo); +console.log('deserialized object: ', photo); // now check serialization let newPhotoJson = classToPlain(photo); -console.log("serialized object: " , newPhotoJson); +console.log('serialized object: ', newPhotoJson); // try to deserialize an array -console.log("-------------------------------"); +console.log('-------------------------------'); -let photosJson = [{ - id: "1", - filename: "myphoto.jpg", - description: "about my photo", +let photosJson = [ + { + id: '1', + filename: 'myphoto.jpg', + description: 'about my photo', author: { - id: "2", - firstName: "Johny", - lastName: "Cage", - registrationDate: "1995-12-17T03:24:00" + id: '2', + firstName: 'Johny', + lastName: 'Cage', + registrationDate: '1995-12-17T03:24:00', }, - albums: [{ - id: "1", - name: "My life" - }, - { - id: "2", - name: "My young years" - }] -}, -{ - id: "2", - filename: "hisphoto.jpg", - description: "about his photo", + albums: [ + { + id: '1', + name: 'My life', + }, + { + id: '2', + name: 'My young years', + }, + ], + }, + { + id: '2', + filename: 'hisphoto.jpg', + description: 'about his photo', author: { - id: "2", - firstName: "Johny", - lastName: "Cage" + id: '2', + firstName: 'Johny', + lastName: 'Cage', }, - albums: [{ - id: "1", - name: "My life" - }, - { - id: "2", - name: "My young years" - }] -}]; + albums: [ + { + id: '1', + name: 'My life', + }, + { + id: '2', + name: 'My young years', + }, + ], + }, +]; let photos = plainToClass(Photo, photosJson); -console.log("deserialized array: " , photos); +console.log('deserialized array: ', photos); // now check array serialization let newPhotosJson = classToPlain(photos); -console.log("serialized array: " , newPhotosJson); \ No newline at end of file +console.log('serialized array: ', newPhotosJson); diff --git a/sample/sample3-custom-arrays/Album.ts b/sample/sample3-custom-arrays/Album.ts index 2ed4bf67e..1df4f58fb 100644 --- a/sample/sample3-custom-arrays/Album.ts +++ b/sample/sample3-custom-arrays/Album.ts @@ -1,8 +1,5 @@ - export class Album { + id: string; - id: string; - - name: string; - -} \ No newline at end of file + name: string; +} diff --git a/sample/sample3-custom-arrays/AlbumArray.ts b/sample/sample3-custom-arrays/AlbumArray.ts index 76fff6533..79c7d297d 100644 --- a/sample/sample3-custom-arrays/AlbumArray.ts +++ b/sample/sample3-custom-arrays/AlbumArray.ts @@ -1,9 +1,7 @@ -import {Album} from "./Album"; +import { Album } from './Album'; export class AlbumArray extends Array { - - findByName(name: string) { - return this.find(album => album.name === name); - } - -} \ No newline at end of file + findByName(name: string) { + return this.find(album => album.name === name); + } +} diff --git a/sample/sample3-custom-arrays/Photo.ts b/sample/sample3-custom-arrays/Photo.ts index 27f4a8d0a..75a89efa5 100644 --- a/sample/sample3-custom-arrays/Photo.ts +++ b/sample/sample3-custom-arrays/Photo.ts @@ -1,18 +1,16 @@ -import {Album} from "./Album"; -import {AlbumArray} from "./AlbumArray"; -import {Type} from "../../src/decorators"; +import { Album } from './Album'; +import { AlbumArray } from './AlbumArray'; +import { Type } from '../../src/decorators'; export class Photo { + id: string; - id: string; + filename: string; - filename: string; + description: string; - description: string; + tags: string[]; - tags: string[]; - - @Type(() => Album) - albums: AlbumArray; - -} \ No newline at end of file + @Type(() => Album) + albums: AlbumArray; +} diff --git a/sample/sample3-custom-arrays/app.ts b/sample/sample3-custom-arrays/app.ts index 443d21ded..2b7f26e73 100644 --- a/sample/sample3-custom-arrays/app.ts +++ b/sample/sample3-custom-arrays/app.ts @@ -1,36 +1,35 @@ -import "es6-shim"; -import "reflect-metadata"; -import {classToPlain, plainToClass} from "../../src/index"; -import {Photo} from "./Photo"; +import 'es6-shim'; +import 'reflect-metadata'; +import { classToPlain, plainToClass } from '../../src/index'; +import { Photo } from './Photo'; // check deserialization let photoJson = { - id: "1", - filename: "myphoto.jpg", - description: "about my photo", - tags: [ - "me", - "iam" - ], - albums: [{ - id: "1", - name: "My life" + id: '1', + filename: 'myphoto.jpg', + description: 'about my photo', + tags: ['me', 'iam'], + albums: [ + { + id: '1', + name: 'My life', }, { - id: "2", - name: "My young years" - }] + id: '2', + name: 'My young years', + }, + ], }; let photo = plainToClass(Photo, photoJson); -console.log("deserialized object: " , photo); -console.log("-----------------------------"); -console.log("Trying to find album: ", photo.albums.findByName("My life")); -console.log("-----------------------------"); +console.log('deserialized object: ', photo); +console.log('-----------------------------'); +console.log('Trying to find album: ', photo.albums.findByName('My life')); +console.log('-----------------------------'); // now check serialization let newPhotoJson = classToPlain(photo); -console.log("serialized object: " , newPhotoJson); -console.log("-----------------------------"); +console.log('serialized object: ', newPhotoJson); +console.log('-----------------------------'); diff --git a/sample/sample4-generics/SimpleCollection.ts b/sample/sample4-generics/SimpleCollection.ts index 19fcd4ec7..dec60128b 100644 --- a/sample/sample4-generics/SimpleCollection.ts +++ b/sample/sample4-generics/SimpleCollection.ts @@ -1,7 +1,4 @@ - export class SimpleCollection { - - items: T[]; - count: number; - -} \ No newline at end of file + items: T[]; + count: number; +} diff --git a/sample/sample4-generics/SuperCollection.ts b/sample/sample4-generics/SuperCollection.ts index a31ff0338..8911b1954 100644 --- a/sample/sample4-generics/SuperCollection.ts +++ b/sample/sample4-generics/SuperCollection.ts @@ -1,19 +1,17 @@ -import {Type, Exclude} from "../../src/decorators"; +import { Type, Exclude } from '../../src/decorators'; export class SuperCollection { - - @Exclude() - private type: Function; + @Exclude() + private type: Function; - @Type(options => { - return (options.newObject as SuperCollection).type; - }) - items: T[]; - - count: number; - - constructor(type: Function) { - this.type = type; - } - -} \ No newline at end of file + @Type(options => { + return (options.newObject as SuperCollection).type; + }) + items: T[]; + + count: number; + + constructor(type: Function) { + this.type = type; + } +} diff --git a/sample/sample4-generics/User.ts b/sample/sample4-generics/User.ts index 3077a4a85..ae78fd2af 100644 --- a/sample/sample4-generics/User.ts +++ b/sample/sample4-generics/User.ts @@ -1,25 +1,23 @@ -import {Exclude} from "../../src/decorators"; +import { Exclude } from '../../src/decorators'; export class User { + id: number; - id: number; - - firstName: string; - - lastName: string; - - @Exclude() - password: string; - - constructor(id: number, firstName: string, lastName: string, password: string) { - this.id = id; - this.firstName = firstName; - this.lastName = lastName; - this.password = password; - } - - get name() { - return this.firstName + " " + this.lastName; - } + firstName: string; -} \ No newline at end of file + lastName: string; + + @Exclude() + password: string; + + constructor(id: number, firstName: string, lastName: string, password: string) { + this.id = id; + this.firstName = firstName; + this.lastName = lastName; + this.password = password; + } + + get name() { + return this.firstName + ' ' + this.lastName; + } +} diff --git a/sample/sample4-generics/app.ts b/sample/sample4-generics/app.ts index ae4cc8cf2..db23949a3 100644 --- a/sample/sample4-generics/app.ts +++ b/sample/sample4-generics/app.ts @@ -1,15 +1,12 @@ -import "es6-shim"; -import "reflect-metadata"; -import {SimpleCollection} from "./SimpleCollection"; -import {User} from "./User"; -import {classToPlain, plainToClass, plainToClassFromExist} from "../../src/index"; -import {SuperCollection} from "./SuperCollection"; +import 'es6-shim'; +import 'reflect-metadata'; +import { SimpleCollection } from './SimpleCollection'; +import { User } from './User'; +import { classToPlain, plainToClass, plainToClassFromExist } from '../../src/index'; +import { SuperCollection } from './SuperCollection'; let collection = new SimpleCollection(); -collection.items = [ - new User(1, "Johny", "Cage", "*******"), - new User(2, "Dima", "Cage", "*******") -]; +collection.items = [new User(1, 'Johny', 'Cage', '*******'), new User(2, 'Dima', 'Cage', '*******')]; collection.count = 2; // using generics works only for classToPlain operations, since in runtime we can @@ -20,17 +17,20 @@ collection.count = 2; // alternatively you can use factory method let collectionJson = { - items: [{ - id: 1, - firstName: "Johny", - lastName: "Cage", - password: "*******", - }, { - id: 2, - firstName: "Dima", - lastName: "Cage", - password: "*******", - }] + items: [ + { + id: 1, + firstName: 'Johny', + lastName: 'Cage', + password: '*******', + }, + { + id: 2, + firstName: 'Dima', + lastName: 'Cage', + password: '*******', + }, + ], }; -console.log(plainToClassFromExist(new SuperCollection(User), collectionJson)); \ No newline at end of file +console.log(plainToClassFromExist(new SuperCollection(User), collectionJson)); diff --git a/sample/sample5-custom-transformer/User.ts b/sample/sample5-custom-transformer/User.ts index 35631d79e..b508eb012 100644 --- a/sample/sample5-custom-transformer/User.ts +++ b/sample/sample5-custom-transformer/User.ts @@ -1,15 +1,13 @@ -import {Type, Transform} from "../../src/decorators"; -import * as moment from "moment"; +import { Type, Transform } from '../../src/decorators'; +import * as moment from 'moment'; export class User { + id: number; - id: number; + name: string; - name: string; - - @Type(() => Date) - @Transform(value => value.toString(), { toPlainOnly: true }) - @Transform(value => moment(value), { toClassOnly: true }) - date: Date; - -} \ No newline at end of file + @Type(() => Date) + @Transform(value => value.toString(), { toPlainOnly: true }) + @Transform(value => moment(value), { toClassOnly: true }) + date: Date; +} diff --git a/sample/sample5-custom-transformer/app.ts b/sample/sample5-custom-transformer/app.ts index 710d71de6..2ca04ea17 100644 --- a/sample/sample5-custom-transformer/app.ts +++ b/sample/sample5-custom-transformer/app.ts @@ -1,19 +1,19 @@ -import "es6-shim"; -import "reflect-metadata"; -import {plainToClass, classToPlain} from "../../src/index"; -import {User} from "./User"; +import 'es6-shim'; +import 'reflect-metadata'; +import { plainToClass, classToPlain } from '../../src/index'; +import { User } from './User'; let userJson = { - id: 1, - name: "Johny Cage", - date: new Date().valueOf() + id: 1, + name: 'Johny Cage', + date: new Date().valueOf(), }; console.log(plainToClass(User, userJson)); const user = new User(); user.id = 1; -user.name = "Johny Cage"; +user.name = 'Johny Cage'; user.date = new Date(); console.log(classToPlain(user)); diff --git a/src/ClassTransformOptions.ts b/src/ClassTransformOptions.ts index 28dee8830..2758c6b61 100644 --- a/src/ClassTransformOptions.ts +++ b/src/ClassTransformOptions.ts @@ -3,75 +3,72 @@ * This is useful when you have external classes. */ export interface TargetMap { + /** + * Target which Types are being specified. + */ + target: Function; - /** - * Target which Types are being specified. - */ - target: Function; - - /** - * List of properties and their Types. - */ - properties: { [key: string]: Function }; + /** + * List of properties and their Types. + */ + properties: { [key: string]: Function }; } /** * Options to be passed during transformation. */ export interface ClassTransformOptions { + /** + * Exclusion strategy. By default exposeAll is used, which means that it will expose all properties are transformed + * by default. + */ + strategy?: 'excludeAll' | 'exposeAll'; - /** - * Exclusion strategy. By default exposeAll is used, which means that it will expose all properties are transformed - * by default. - */ - strategy?: "excludeAll"|"exposeAll"; - - /** - * Indicates if extraneous properties should be excluded from the value when converting a plain value to a class. - */ - excludeExtraneousValues?: boolean; + /** + * Indicates if extraneous properties should be excluded from the value when converting a plain value to a class. + */ + excludeExtraneousValues?: boolean; - /** - * Only properties with given groups gonna be transformed. - */ - groups?: string[]; + /** + * Only properties with given groups gonna be transformed. + */ + groups?: string[]; - /** - * Only properties with "since" > version < "until" gonna be transformed. - */ - version?: number; + /** + * Only properties with "since" > version < "until" gonna be transformed. + */ + version?: number; - /** - * Excludes properties with the given prefixes. For example, if you mark your private properties with "_" and "__" - * you can set this option's value to ["_", "__"] and all private properties will be skipped. - * This works only for "exposeAll" strategy. - */ - excludePrefixes?: string[]; + /** + * Excludes properties with the given prefixes. For example, if you mark your private properties with "_" and "__" + * you can set this option's value to ["_", "__"] and all private properties will be skipped. + * This works only for "exposeAll" strategy. + */ + excludePrefixes?: string[]; - /** - * If set to true then class transformer will ignore all @Expose and @Exclude decorators and what inside them. - * This option is useful if you want to kinda clone your object but do not apply decorators affects. - */ - ignoreDecorators?: boolean; + /** + * If set to true then class transformer will ignore all @Expose and @Exclude decorators and what inside them. + * This option is useful if you want to kinda clone your object but do not apply decorators affects. + */ + ignoreDecorators?: boolean; - /** - * Target maps allows to set a Types of the transforming object without using @Type decorator. - * This is useful when you are transforming external classes, or if you already have type metadata for - * objects and you don't want to set it up again. - */ - targetMaps?: TargetMap[]; + /** + * Target maps allows to set a Types of the transforming object without using @Type decorator. + * This is useful when you are transforming external classes, or if you already have type metadata for + * objects and you don't want to set it up again. + */ + targetMaps?: TargetMap[]; + /** + * If set to true then class transformer will perform a circular check. (circular check is turned off by default) + * This option is useful when you know for sure that your types might have a circular dependency. + */ + enableCircularCheck?: boolean; - /** - * If set to true then class transformer will perform a circular check. (circular check is turned off by default) - * This option is useful when you know for sure that your types might have a circular dependency. - */ - enableCircularCheck?: boolean; - - /** - * If set to true then class transformer will try to convert properties implicitly to their target type based on their typing information. - * - * DEFAULT: `false` - */ - enableImplicitConversion?: boolean; -} \ No newline at end of file + /** + * If set to true then class transformer will try to convert properties implicitly to their target type based on their typing information. + * + * DEFAULT: `false` + */ + enableImplicitConversion?: boolean; +} diff --git a/src/ClassTransformer.ts b/src/ClassTransformer.ts index 929a1996f..521173c17 100644 --- a/src/ClassTransformer.ts +++ b/src/ClassTransformer.ts @@ -1,105 +1,134 @@ -import {ClassTransformOptions} from "./ClassTransformOptions"; -import {TransformOperationExecutor, TransformationType} from "./TransformOperationExecutor"; +import { ClassTransformOptions } from './ClassTransformOptions'; +import { TransformOperationExecutor, TransformationType } from './TransformOperationExecutor'; export type ClassType = { - new (...args: any[]): T; + new (...args: any[]): T; }; export class ClassTransformer { + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // Public Methods - // ------------------------------------------------------------------------- + /** + * Converts class (constructor) object to plain (literal) object. Also works with arrays. + */ + classToPlain>(object: T, options?: ClassTransformOptions): Record; + classToPlain>(object: T[], options?: ClassTransformOptions): Record[]; + classToPlain>( + object: T | T[], + options?: ClassTransformOptions + ): Record | Record[] { + const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_PLAIN, options || {}); + return executor.transform(undefined, object, undefined, undefined, undefined, undefined); + } - /** - * Converts class (constructor) object to plain (literal) object. Also works with arrays. - */ - classToPlain>(object: T, options?: ClassTransformOptions): Record; - classToPlain>(object: T[], options?: ClassTransformOptions): Record[]; - classToPlain>(object: T|T[], options?: ClassTransformOptions): Record|Record[] { - const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_PLAIN, options || {}); - return executor.transform(undefined, object, undefined, undefined, undefined, undefined); - } + /** + * Converts class (constructor) object to plain (literal) object. + * Uses given plain object as source object (it means fills given plain object with data from class object). + * Also works with arrays. + */ + classToPlainFromExist, P>( + object: T, + plainObject: P, + options?: ClassTransformOptions + ): T; + classToPlainFromExist, P>( + object: T, + plainObjects: P[], + options?: ClassTransformOptions + ): T[]; + classToPlainFromExist, P>( + object: T, + plainObject: P | P[], + options?: ClassTransformOptions + ): T | T[] { + const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_PLAIN, options || {}); + return executor.transform(plainObject, object, undefined, undefined, undefined, undefined); + } - /** - * Converts class (constructor) object to plain (literal) object. - * Uses given plain object as source object (it means fills given plain object with data from class object). - * Also works with arrays. - */ - classToPlainFromExist, P>(object: T, plainObject: P, options?: ClassTransformOptions): T; - classToPlainFromExist, P>(object: T, plainObjects: P[], options?: ClassTransformOptions): T[]; - classToPlainFromExist, P>(object: T, plainObject: P|P[], options?: ClassTransformOptions): T|T[] { - const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_PLAIN, options || {}); - return executor.transform(plainObject, object, undefined, undefined, undefined, undefined); - } + /** + * Converts plain (literal) object to class (constructor) object. Also works with arrays. + */ + plainToClass, V extends Array>( + cls: ClassType, + plain: V, + options?: ClassTransformOptions + ): T[]; + plainToClass, V>(cls: ClassType, plain: V, options?: ClassTransformOptions): T; + plainToClass, V>( + cls: ClassType, + plain: V | V[], + options?: ClassTransformOptions + ): T | T[] { + const executor = new TransformOperationExecutor(TransformationType.PLAIN_TO_CLASS, options || {}); + return executor.transform(undefined, plain, cls, undefined, undefined, undefined); + } - /** - * Converts plain (literal) object to class (constructor) object. Also works with arrays. - */ - plainToClass, V extends Array>(cls: ClassType, plain: V, options?: ClassTransformOptions): T[]; - plainToClass, V>(cls: ClassType, plain: V, options?: ClassTransformOptions): T; - plainToClass, V>(cls: ClassType, plain: V|V[], options?: ClassTransformOptions): T|T[] { - const executor = new TransformOperationExecutor(TransformationType.PLAIN_TO_CLASS, options || {}); - return executor.transform(undefined, plain, cls, undefined, undefined, undefined); - } + /** + * Converts plain (literal) object to class (constructor) object. + * Uses given object as source object (it means fills given object with data from plain object). + * Also works with arrays. + */ + plainToClassFromExist, V extends Array>( + clsObject: T, + plain: V, + options?: ClassTransformOptions + ): T; + plainToClassFromExist, V>(clsObject: T, plain: V, options?: ClassTransformOptions): T[]; + plainToClassFromExist, V>( + clsObject: T, + plain: V | V[], + options?: ClassTransformOptions + ): T | T[] { + const executor = new TransformOperationExecutor(TransformationType.PLAIN_TO_CLASS, options || {}); + return executor.transform(clsObject, plain, undefined, undefined, undefined, undefined); + } - /** - * Converts plain (literal) object to class (constructor) object. - * Uses given object as source object (it means fills given object with data from plain object). - * Also works with arrays. - */ - plainToClassFromExist, V extends Array>(clsObject: T, plain: V, options?: ClassTransformOptions): T; - plainToClassFromExist, V>(clsObject: T, plain: V, options?: ClassTransformOptions): T[]; - plainToClassFromExist, V>(clsObject: T, plain: V|V[], options?: ClassTransformOptions): T|T[] { - const executor = new TransformOperationExecutor(TransformationType.PLAIN_TO_CLASS, options || {}); - return executor.transform(clsObject, plain, undefined, undefined, undefined, undefined); - } + /** + * Converts class (constructor) object to new class (constructor) object. Also works with arrays. + */ + classToClass(object: T, options?: ClassTransformOptions): T; + classToClass(object: T[], options?: ClassTransformOptions): T[]; + classToClass(object: T | T[], options?: ClassTransformOptions): T | T[] { + const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_CLASS, options || {}); + return executor.transform(undefined, object, undefined, undefined, undefined, undefined); + } - /** - * Converts class (constructor) object to new class (constructor) object. Also works with arrays. - */ - classToClass(object: T, options?: ClassTransformOptions): T; - classToClass(object: T[], options?: ClassTransformOptions): T[]; - classToClass(object: T|T[], options?: ClassTransformOptions): T|T[] { - const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_CLASS, options || {}); - return executor.transform(undefined, object, undefined, undefined, undefined, undefined); - } + /** + * Converts class (constructor) object to plain (literal) object. + * Uses given plain object as source object (it means fills given plain object with data from class object). + * Also works with arrays. + */ + classToClassFromExist(object: T, fromObject: T, options?: ClassTransformOptions): T; + classToClassFromExist(object: T, fromObjects: T[], options?: ClassTransformOptions): T[]; + classToClassFromExist(object: T, fromObject: T | T[], options?: ClassTransformOptions): T | T[] { + const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_CLASS, options || {}); + return executor.transform(fromObject, object, undefined, undefined, undefined, undefined); + } - /** - * Converts class (constructor) object to plain (literal) object. - * Uses given plain object as source object (it means fills given plain object with data from class object). - * Also works with arrays. - */ - classToClassFromExist(object: T, fromObject: T, options?: ClassTransformOptions): T; - classToClassFromExist(object: T, fromObjects: T[], options?: ClassTransformOptions): T[]; - classToClassFromExist(object: T, fromObject: T|T[], options?: ClassTransformOptions): T|T[] { - const executor = new TransformOperationExecutor(TransformationType.CLASS_TO_CLASS, options || {}); - return executor.transform(fromObject, object, undefined, undefined, undefined, undefined); - } + /** + * Serializes given object to a JSON string. + */ + serialize(object: T, options?: ClassTransformOptions): string; + serialize(object: T[], options?: ClassTransformOptions): string; + serialize(object: T | T[], options?: ClassTransformOptions): string { + return JSON.stringify(this.classToPlain(object, options)); + } - /** - * Serializes given object to a JSON string. - */ - serialize(object: T, options?: ClassTransformOptions): string; - serialize(object: T[], options?: ClassTransformOptions): string; - serialize(object: T|T[], options?: ClassTransformOptions): string { - return JSON.stringify(this.classToPlain(object, options)); - } - - /** - * Deserializes given JSON string to a object of the given class. - */ - deserialize(cls: ClassType, json: string, options?: ClassTransformOptions): T { - const jsonObject: T = JSON.parse(json); - return this.plainToClass(cls, jsonObject, options); - } - - /** - * Deserializes given JSON string to an array of objects of the given class. - */ - deserializeArray(cls: ClassType, json: string, options?: ClassTransformOptions): T[] { - const jsonObject: any[] = JSON.parse(json); - return this.plainToClass(cls, jsonObject, options); - } + /** + * Deserializes given JSON string to a object of the given class. + */ + deserialize(cls: ClassType, json: string, options?: ClassTransformOptions): T { + const jsonObject: T = JSON.parse(json); + return this.plainToClass(cls, jsonObject, options); + } + /** + * Deserializes given JSON string to an array of objects of the given class. + */ + deserializeArray(cls: ClassType, json: string, options?: ClassTransformOptions): T[] { + const jsonObject: any[] = JSON.parse(json); + return this.plainToClass(cls, jsonObject, options); + } } diff --git a/src/decorators.ts b/src/decorators.ts index 62abbb528..a26de57a0 100644 --- a/src/decorators.ts +++ b/src/decorators.ts @@ -1,21 +1,30 @@ -import {ClassTransformer} from "./ClassTransformer"; -import {defaultMetadataStorage} from "./storage"; -import {TypeMetadata} from "./metadata/TypeMetadata"; -import {ExposeMetadata} from "./metadata/ExposeMetadata"; -import {ExposeOptions, ExcludeOptions, TypeHelpOptions, TransformOptions, TypeOptions} from "./metadata/ExposeExcludeOptions"; -import {ExcludeMetadata} from "./metadata/ExcludeMetadata"; -import {TransformMetadata} from "./metadata/TransformMetadata"; -import {ClassTransformOptions} from "./ClassTransformOptions"; -import {TransformationType} from "./TransformOperationExecutor"; +import { ClassTransformer } from './ClassTransformer'; +import { defaultMetadataStorage } from './storage'; +import { TypeMetadata } from './metadata/TypeMetadata'; +import { ExposeMetadata } from './metadata/ExposeMetadata'; +import { + ExposeOptions, + ExcludeOptions, + TypeHelpOptions, + TransformOptions, + TypeOptions, +} from './metadata/ExposeExcludeOptions'; +import { ExcludeMetadata } from './metadata/ExcludeMetadata'; +import { TransformMetadata } from './metadata/TransformMetadata'; +import { ClassTransformOptions } from './ClassTransformOptions'; +import { TransformationType } from './TransformOperationExecutor'; /** * Defines a custom logic for value transformation. */ -export function Transform(transformFn: (value: any, obj: any, transformationType: TransformationType) => any, options?: TransformOptions) { - return function(target: any, key: string): void { - const metadata = new TransformMetadata(target.constructor, key, transformFn, options); - defaultMetadataStorage.addTransformMetadata(metadata); - }; +export function Transform( + transformFn: (value: any, obj: any, transformationType: TransformationType) => any, + options?: TransformOptions +) { + return function (target: any, key: string): void { + const metadata = new TransformMetadata(target.constructor, key, transformFn, options); + defaultMetadataStorage.addTransformMetadata(metadata); + }; } /** @@ -23,11 +32,11 @@ export function Transform(transformFn: (value: any, obj: any, transformationType * The given TypeFunction can return a constructor. A discriminator can be given in the options. */ export function Type(typeFunction?: (type?: TypeHelpOptions) => Function, options?: TypeOptions) { - return function(target: any, key: string): void { - const type = (Reflect as any).getMetadata("design:type", target, key); - const metadata = new TypeMetadata(target.constructor, key, type, typeFunction, options); - defaultMetadataStorage.addTypeMetadata(metadata); - }; + return function (target: any, key: string): void { + const type = (Reflect as any).getMetadata('design:type', target, key); + const metadata = new TypeMetadata(target.constructor, key, type, typeFunction, options); + defaultMetadataStorage.addTypeMetadata(metadata); + }; } /** @@ -36,10 +45,14 @@ export function Type(typeFunction?: (type?: TypeHelpOptions) => Function, option * you want to skip this property. */ export function Expose(options?: ExposeOptions) { - return function(object: Record|Function, propertyName?: string): void { - const metadata = new ExposeMetadata(object instanceof Function ? object : object.constructor, propertyName, options || {}); - defaultMetadataStorage.addExposeMetadata(metadata); - }; + return function (object: Record | Function, propertyName?: string): void { + const metadata = new ExposeMetadata( + object instanceof Function ? object : object.constructor, + propertyName, + options || {} + ); + defaultMetadataStorage.addExposeMetadata(metadata); + }; } /** @@ -48,59 +61,69 @@ export function Expose(options?: ExposeOptions) { * you want to skip this property. */ export function Exclude(options?: ExcludeOptions) { - return function(object: Record|Function, propertyName?: string): void { - const metadata = new ExcludeMetadata(object instanceof Function ? object : object.constructor, propertyName, options || {}); - defaultMetadataStorage.addExcludeMetadata(metadata); - }; + return function (object: Record | Function, propertyName?: string): void { + const metadata = new ExcludeMetadata( + object instanceof Function ? object : object.constructor, + propertyName, + options || {} + ); + defaultMetadataStorage.addExcludeMetadata(metadata); + }; } /** * Transform the object from class to plain object and return only with the exposed properties. */ export function TransformClassToPlain(params?: ClassTransformOptions): Function { + return function (target: Function, propertyKey: string, descriptor: PropertyDescriptor): void { + const classTransformer: ClassTransformer = new ClassTransformer(); + const originalMethod = descriptor.value; - return function (target: Function, propertyKey: string, descriptor: PropertyDescriptor): void { - const classTransformer: ClassTransformer = new ClassTransformer(); - const originalMethod = descriptor.value; - - descriptor.value = function(...args: any[]): Record { - const result: any = originalMethod.apply(this, args); - const isPromise = !!result && (typeof result === "object" || typeof result === "function") && typeof result.then === "function"; - return isPromise ? result.then((data: any) => classTransformer.classToPlain(data, params)) : classTransformer.classToPlain(result, params); - }; + descriptor.value = function (...args: any[]): Record { + const result: any = originalMethod.apply(this, args); + const isPromise = + !!result && (typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function'; + return isPromise + ? result.then((data: any) => classTransformer.classToPlain(data, params)) + : classTransformer.classToPlain(result, params); }; + }; } /** * Return the class instance only with the exposed properties. */ export function TransformClassToClass(params?: ClassTransformOptions): Function { + return function (target: Function, propertyKey: string, descriptor: PropertyDescriptor): void { + const classTransformer: ClassTransformer = new ClassTransformer(); + const originalMethod = descriptor.value; - return function (target: Function, propertyKey: string, descriptor: PropertyDescriptor): void { - const classTransformer: ClassTransformer = new ClassTransformer(); - const originalMethod = descriptor.value; - - descriptor.value = function(...args: any[]): Record { - const result: any = originalMethod.apply(this, args); - const isPromise = !!result && (typeof result === "object" || typeof result === "function") && typeof result.then === "function"; - return isPromise ? result.then((data: any) => classTransformer.classToClass(data, params)) : classTransformer.classToClass(result, params); - }; + descriptor.value = function (...args: any[]): Record { + const result: any = originalMethod.apply(this, args); + const isPromise = + !!result && (typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function'; + return isPromise + ? result.then((data: any) => classTransformer.classToClass(data, params)) + : classTransformer.classToClass(result, params); }; + }; } /** * Return the class instance only with the exposed properties. */ export function TransformPlainToClass(classType: any, params?: ClassTransformOptions): Function { + return function (target: Function, propertyKey: string, descriptor: PropertyDescriptor): void { + const classTransformer: ClassTransformer = new ClassTransformer(); + const originalMethod = descriptor.value; - return function (target: Function, propertyKey: string, descriptor: PropertyDescriptor): void { - const classTransformer: ClassTransformer = new ClassTransformer(); - const originalMethod = descriptor.value; - - descriptor.value = function(...args: any[]): Record { - const result: any = originalMethod.apply(this, args); - const isPromise = !!result && (typeof result === "object" || typeof result === "function") && typeof result.then === "function"; - return isPromise ? result.then((data: any) => classTransformer.plainToClass(classType, data, params)) : classTransformer.plainToClass(classType, result, params); - }; + descriptor.value = function (...args: any[]): Record { + const result: any = originalMethod.apply(this, args); + const isPromise = + !!result && (typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function'; + return isPromise + ? result.then((data: any) => classTransformer.plainToClass(classType, data, params)) + : classTransformer.plainToClass(classType, result, params); }; + }; } diff --git a/src/index.ts b/src/index.ts index 926b2bf9b..4075aed31 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,10 @@ -import {ClassTransformer, ClassType} from "./ClassTransformer"; -import {ClassTransformOptions} from "./ClassTransformOptions"; +import { ClassTransformer, ClassType } from './ClassTransformer'; +import { ClassTransformOptions } from './ClassTransformOptions'; -export {ClassTransformer} from "./ClassTransformer"; -export {ClassTransformOptions} from "./ClassTransformOptions"; -export * from "./metadata/ExposeExcludeOptions"; -export * from "./decorators"; +export { ClassTransformer } from './ClassTransformer'; +export { ClassTransformOptions } from './ClassTransformOptions'; +export * from './metadata/ExposeExcludeOptions'; +export * from './decorators'; const classTransformer = new ClassTransformer(); @@ -13,8 +13,11 @@ const classTransformer = new ClassTransformer(); */ export function classToPlain(object: T, options?: ClassTransformOptions): Record; export function classToPlain(object: T[], options?: ClassTransformOptions): Record[]; -export function classToPlain(object: T|T[], options?: ClassTransformOptions): Record|Record[] { - return classTransformer.classToPlain(object, options); +export function classToPlain( + object: T | T[], + options?: ClassTransformOptions +): Record | Record[] { + return classTransformer.classToPlain(object, options); } /** @@ -22,10 +25,22 @@ export function classToPlain(object: T|T[], options?: ClassTransformOptions): * Uses given plain object as source object (it means fills given plain object with data from class object). * Also works with arrays. */ -export function classToPlainFromExist(object: T, plainObject: Record, options?: ClassTransformOptions): Record; -export function classToPlainFromExist(object: T, plainObjects: Record[], options?: ClassTransformOptions): Record[]; -export function classToPlainFromExist(object: T, plainObject: Record|Record[], options?: ClassTransformOptions): Record|Record[] { - return classTransformer.classToPlainFromExist(object, plainObject, options); +export function classToPlainFromExist( + object: T, + plainObject: Record, + options?: ClassTransformOptions +): Record; +export function classToPlainFromExist( + object: T, + plainObjects: Record[], + options?: ClassTransformOptions +): Record[]; +export function classToPlainFromExist( + object: T, + plainObject: Record | Record[], + options?: ClassTransformOptions +): Record | Record[] { + return classTransformer.classToPlainFromExist(object, plainObject, options); } /** @@ -33,8 +48,8 @@ export function classToPlainFromExist(object: T, plainObject: Record(cls: ClassType, plain: V[], options?: ClassTransformOptions): T[]; export function plainToClass(cls: ClassType, plain: V, options?: ClassTransformOptions): T; -export function plainToClass(cls: ClassType, plain: V|V[], options?: ClassTransformOptions): T|T[] { - return classTransformer.plainToClass(cls, plain as any, options); +export function plainToClass(cls: ClassType, plain: V | V[], options?: ClassTransformOptions): T | T[] { + return classTransformer.plainToClass(cls, plain as any, options); } /** @@ -44,8 +59,8 @@ export function plainToClass(cls: ClassType, plain: V|V[], options?: Cl */ export function plainToClassFromExist(clsObject: T[], plain: V[], options?: ClassTransformOptions): T[]; export function plainToClassFromExist(clsObject: T, plain: V, options?: ClassTransformOptions): T; -export function plainToClassFromExist(clsObject: T, plain: V|V[], options?: ClassTransformOptions): T|T[] { - return classTransformer.plainToClassFromExist(clsObject, plain, options); +export function plainToClassFromExist(clsObject: T, plain: V | V[], options?: ClassTransformOptions): T | T[] { + return classTransformer.plainToClassFromExist(clsObject, plain, options); } /** @@ -53,8 +68,8 @@ export function plainToClassFromExist(clsObject: T, plain: V|V[], options? */ export function classToClass(object: T, options?: ClassTransformOptions): T; export function classToClass(object: T[], options?: ClassTransformOptions): T[]; -export function classToClass(object: T|T[], options?: ClassTransformOptions): T|T[] { - return classTransformer.classToClass(object, options); +export function classToClass(object: T | T[], options?: ClassTransformOptions): T | T[] { + return classTransformer.classToClass(object, options); } /** @@ -64,8 +79,8 @@ export function classToClass(object: T|T[], options?: ClassTransformOptions): */ export function classToClassFromExist(object: T, fromObject: T, options?: ClassTransformOptions): T; export function classToClassFromExist(object: T, fromObjects: T[], options?: ClassTransformOptions): T[]; -export function classToClassFromExist(object: T, fromObject: T|T[], options?: ClassTransformOptions): T|T[] { - return classTransformer.classToClassFromExist(object, fromObject, options); +export function classToClassFromExist(object: T, fromObject: T | T[], options?: ClassTransformOptions): T | T[] { + return classTransformer.classToClassFromExist(object, fromObject, options); } /** @@ -73,22 +88,22 @@ export function classToClassFromExist(object: T, fromObject: T|T[], options?: */ export function serialize(object: T, options?: ClassTransformOptions): string; export function serialize(object: T[], options?: ClassTransformOptions): string; -export function serialize(object: T|T[], options?: ClassTransformOptions): string { - return classTransformer.serialize(object, options); +export function serialize(object: T | T[], options?: ClassTransformOptions): string { + return classTransformer.serialize(object, options); } /** * Deserializes given JSON string to a object of the given class. */ export function deserialize(cls: ClassType, json: string, options?: ClassTransformOptions): T { - return classTransformer.deserialize(cls, json, options); + return classTransformer.deserialize(cls, json, options); } /** * Deserializes given JSON string to an array of objects of the given class. */ export function deserializeArray(cls: ClassType, json: string, options?: ClassTransformOptions): T[] { - return classTransformer.deserializeArray(cls, json, options); + return classTransformer.deserializeArray(cls, json, options); } /** @@ -96,7 +111,7 @@ export function deserializeArray(cls: ClassType, json: string, options?: C */ export enum TransformationType { - PLAIN_TO_CLASS, - CLASS_TO_PLAIN, - CLASS_TO_CLASS + PLAIN_TO_CLASS, + CLASS_TO_PLAIN, + CLASS_TO_CLASS, } diff --git a/src/metadata/ExcludeMetadata.ts b/src/metadata/ExcludeMetadata.ts index f0eb19720..98b090294 100644 --- a/src/metadata/ExcludeMetadata.ts +++ b/src/metadata/ExcludeMetadata.ts @@ -1,10 +1,5 @@ -import {ExcludeOptions} from "./ExposeExcludeOptions"; +import { ExcludeOptions } from './ExposeExcludeOptions'; export class ExcludeMetadata { - - constructor(public target: Function, - public propertyName: string, - public options: ExcludeOptions) { - } - -} \ No newline at end of file + constructor(public target: Function, public propertyName: string, public options: ExcludeOptions) {} +} diff --git a/src/metadata/ExposeExcludeOptions.ts b/src/metadata/ExposeExcludeOptions.ts index a44b7e520..59206b361 100644 --- a/src/metadata/ExposeExcludeOptions.ts +++ b/src/metadata/ExposeExcludeOptions.ts @@ -1,46 +1,45 @@ - export interface TransformOptions { - since?: number; - until?: number; - groups?: string[]; - toClassOnly?: boolean; - toPlainOnly?: boolean; + since?: number; + until?: number; + groups?: string[]; + toClassOnly?: boolean; + toPlainOnly?: boolean; } export interface TypeOptions { - discriminator?: Discriminator; - /** - * Is false by default. - */ - keepDiscriminatorProperty?: boolean; + discriminator?: Discriminator; + /** + * Is false by default. + */ + keepDiscriminatorProperty?: boolean; } export interface TypeHelpOptions { - newObject: any; - object: Record; - property: string; + newObject: any; + object: Record; + property: string; } export interface ExposeOptions { - name?: string; - since?: number; - until?: number; - groups?: string[]; - toClassOnly?: boolean; - toPlainOnly?: boolean; + name?: string; + since?: number; + until?: number; + groups?: string[]; + toClassOnly?: boolean; + toPlainOnly?: boolean; } export interface ExcludeOptions { - toClassOnly?: boolean; - toPlainOnly?: boolean; + toClassOnly?: boolean; + toPlainOnly?: boolean; } export interface Discriminator { - property: string; - subTypes: JsonSubType[]; + property: string; + subTypes: JsonSubType[]; } export interface JsonSubType { - value: new (...args: any[]) => any; - name: string; + value: new (...args: any[]) => any; + name: string; } diff --git a/src/metadata/ExposeMetadata.ts b/src/metadata/ExposeMetadata.ts index e3ab755c7..43ce826cd 100644 --- a/src/metadata/ExposeMetadata.ts +++ b/src/metadata/ExposeMetadata.ts @@ -1,10 +1,5 @@ -import {ExposeOptions} from "./ExposeExcludeOptions"; +import { ExposeOptions } from './ExposeExcludeOptions'; export class ExposeMetadata { - - constructor(public target: Function, - public propertyName: string, - public options: ExposeOptions) { - } - -} \ No newline at end of file + constructor(public target: Function, public propertyName: string, public options: ExposeOptions) {} +} diff --git a/src/metadata/MetadataStorage.ts b/src/metadata/MetadataStorage.ts index 9d148e80a..1fc2bfdc4 100644 --- a/src/metadata/MetadataStorage.ts +++ b/src/metadata/MetadataStorage.ts @@ -1,234 +1,257 @@ -import {TypeMetadata} from "./TypeMetadata"; -import {ExposeMetadata} from "./ExposeMetadata"; -import {ExcludeMetadata} from "./ExcludeMetadata"; -import {TransformationType} from "../TransformOperationExecutor"; -import {TransformMetadata} from "./TransformMetadata"; +import { TypeMetadata } from './TypeMetadata'; +import { ExposeMetadata } from './ExposeMetadata'; +import { ExcludeMetadata } from './ExcludeMetadata'; +import { TransformationType } from '../TransformOperationExecutor'; +import { TransformMetadata } from './TransformMetadata'; /** * Storage all library metadata. */ export class MetadataStorage { + // ------------------------------------------------------------------------- + // Properties + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // Properties - // ------------------------------------------------------------------------- + private _typeMetadatas = new Map>(); + private _transformMetadatas = new Map>(); + private _exposeMetadatas = new Map>(); + private _excludeMetadatas = new Map>(); + private _ancestorsMap = new Map(); - private _typeMetadatas = new Map>(); - private _transformMetadatas = new Map>(); - private _exposeMetadatas = new Map>(); - private _excludeMetadatas = new Map>(); - private _ancestorsMap = new Map(); + // ------------------------------------------------------------------------- + // Adder Methods + // ------------------------------------------------------------------------- - // ------------------------------------------------------------------------- - // Adder Methods - // ------------------------------------------------------------------------- - - addTypeMetadata(metadata: TypeMetadata): void { - if (!this._typeMetadatas.has(metadata.target)) { - this._typeMetadatas.set(metadata.target, new Map()); - } - this._typeMetadatas.get(metadata.target).set(metadata.propertyName, metadata); + addTypeMetadata(metadata: TypeMetadata): void { + if (!this._typeMetadatas.has(metadata.target)) { + this._typeMetadatas.set(metadata.target, new Map()); } + this._typeMetadatas.get(metadata.target).set(metadata.propertyName, metadata); + } - addTransformMetadata(metadata: TransformMetadata): void { - if (!this._transformMetadatas.has(metadata.target)) { - this._transformMetadatas.set(metadata.target, new Map()); + addTransformMetadata(metadata: TransformMetadata): void { + if (!this._transformMetadatas.has(metadata.target)) { + this._transformMetadatas.set(metadata.target, new Map()); } - if (!this._transformMetadatas.get(metadata.target).has(metadata.propertyName)) { - this._transformMetadatas.get(metadata.target).set(metadata.propertyName, []); - } - this._transformMetadatas.get(metadata.target).get(metadata.propertyName).push(metadata); + if (!this._transformMetadatas.get(metadata.target).has(metadata.propertyName)) { + this._transformMetadatas.get(metadata.target).set(metadata.propertyName, []); } + this._transformMetadatas.get(metadata.target).get(metadata.propertyName).push(metadata); + } - addExposeMetadata(metadata: ExposeMetadata): void { - if (!this._exposeMetadatas.has(metadata.target)) { - this._exposeMetadatas.set(metadata.target, new Map()); - } - this._exposeMetadatas.get(metadata.target).set(metadata.propertyName, metadata); + addExposeMetadata(metadata: ExposeMetadata): void { + if (!this._exposeMetadatas.has(metadata.target)) { + this._exposeMetadatas.set(metadata.target, new Map()); } + this._exposeMetadatas.get(metadata.target).set(metadata.propertyName, metadata); + } - addExcludeMetadata(metadata: ExcludeMetadata): void { - if (!this._excludeMetadatas.has(metadata.target)) { - this._excludeMetadatas.set(metadata.target, new Map()); - } - this._excludeMetadatas.get(metadata.target).set(metadata.propertyName, metadata); + addExcludeMetadata(metadata: ExcludeMetadata): void { + if (!this._excludeMetadatas.has(metadata.target)) { + this._excludeMetadatas.set(metadata.target, new Map()); } + this._excludeMetadatas.get(metadata.target).set(metadata.propertyName, metadata); + } - // ------------------------------------------------------------------------- - // Public Methods - // ------------------------------------------------------------------------- - - findTransformMetadatas(target: Function, propertyName: string, transformationType: TransformationType): TransformMetadata[] { - return this.findMetadatas(this._transformMetadatas, target, propertyName) - .filter(metadata => { - if (!metadata.options) - return true; - if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) - return true; - - if (metadata.options.toClassOnly === true) { - return transformationType === TransformationType.CLASS_TO_CLASS || transformationType === TransformationType.PLAIN_TO_CLASS; - } - if (metadata.options.toPlainOnly === true) { - return transformationType === TransformationType.CLASS_TO_PLAIN; - } - - return true; - }); - } + // ------------------------------------------------------------------------- + // Public Methods + // ------------------------------------------------------------------------- - findExcludeMetadata(target: Function, propertyName: string): ExcludeMetadata { - return this.findMetadata(this._excludeMetadatas, target, propertyName); - } + findTransformMetadatas( + target: Function, + propertyName: string, + transformationType: TransformationType + ): TransformMetadata[] { + return this.findMetadatas(this._transformMetadatas, target, propertyName).filter(metadata => { + if (!metadata.options) return true; + if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) return true; - findExposeMetadata(target: Function, propertyName: string): ExposeMetadata { - return this.findMetadata(this._exposeMetadatas, target, propertyName); - } + if (metadata.options.toClassOnly === true) { + return ( + transformationType === TransformationType.CLASS_TO_CLASS || + transformationType === TransformationType.PLAIN_TO_CLASS + ); + } + if (metadata.options.toPlainOnly === true) { + return transformationType === TransformationType.CLASS_TO_PLAIN; + } - findExposeMetadataByCustomName(target: Function, name: string): ExposeMetadata { - return this.getExposedMetadatas(target).find(metadata => { - return metadata.options && metadata.options.name === name; - }); - } + return true; + }); + } - findTypeMetadata(target: Function, propertyName: string): TypeMetadata { - return this.findMetadata(this._typeMetadatas, target, propertyName); - } + findExcludeMetadata(target: Function, propertyName: string): ExcludeMetadata { + return this.findMetadata(this._excludeMetadatas, target, propertyName); + } - getStrategy(target: Function): "excludeAll"|"exposeAll"|"none" { - const excludeMap = this._excludeMetadatas.get(target); - const exclude = excludeMap && excludeMap.get(undefined); - const exposeMap = this._exposeMetadatas.get(target); - const expose = exposeMap && exposeMap.get(undefined); - if ((exclude && expose) || (!exclude && !expose)) return "none"; - return exclude ? "excludeAll" : "exposeAll"; - } + findExposeMetadata(target: Function, propertyName: string): ExposeMetadata { + return this.findMetadata(this._exposeMetadatas, target, propertyName); + } - getExposedMetadatas(target: Function): ExposeMetadata[] { - return this.getMetadata(this._exposeMetadatas, target); - } + findExposeMetadataByCustomName(target: Function, name: string): ExposeMetadata { + return this.getExposedMetadatas(target).find(metadata => { + return metadata.options && metadata.options.name === name; + }); + } - getExcludedMetadatas(target: Function): ExcludeMetadata[] { - return this.getMetadata(this._excludeMetadatas, target); - } + findTypeMetadata(target: Function, propertyName: string): TypeMetadata { + return this.findMetadata(this._typeMetadatas, target, propertyName); + } - getExposedProperties(target: Function, transformationType: TransformationType): string[] { - return this.getExposedMetadatas(target) - .filter(metadata => { - if (!metadata.options) - return true; - if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) - return true; - - if (metadata.options.toClassOnly === true) { - return transformationType === TransformationType.CLASS_TO_CLASS || transformationType === TransformationType.PLAIN_TO_CLASS; - } - if (metadata.options.toPlainOnly === true) { - return transformationType === TransformationType.CLASS_TO_PLAIN; - } - - return true; - }) - .map(metadata => metadata.propertyName); - } + getStrategy(target: Function): 'excludeAll' | 'exposeAll' | 'none' { + const excludeMap = this._excludeMetadatas.get(target); + const exclude = excludeMap && excludeMap.get(undefined); + const exposeMap = this._exposeMetadatas.get(target); + const expose = exposeMap && exposeMap.get(undefined); + if ((exclude && expose) || (!exclude && !expose)) return 'none'; + return exclude ? 'excludeAll' : 'exposeAll'; + } - getExcludedProperties(target: Function, transformationType: TransformationType): string[] { - return this.getExcludedMetadatas(target) - .filter(metadata => { - if (!metadata.options) - return true; - if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) - return true; - - if (metadata.options.toClassOnly === true) { - return transformationType === TransformationType.CLASS_TO_CLASS || transformationType === TransformationType.PLAIN_TO_CLASS; - } - if (metadata.options.toPlainOnly === true) { - return transformationType === TransformationType.CLASS_TO_PLAIN; - } - - return true; - }) - .map(metadata => metadata.propertyName); - } + getExposedMetadatas(target: Function): ExposeMetadata[] { + return this.getMetadata(this._exposeMetadatas, target); + } - clear(): void { - this._typeMetadatas.clear(); - this._exposeMetadatas.clear(); - this._excludeMetadatas.clear(); - this._ancestorsMap.clear(); - } + getExcludedMetadatas(target: Function): ExcludeMetadata[] { + return this.getMetadata(this._excludeMetadatas, target); + } - // ------------------------------------------------------------------------- - // Private Methods - // ------------------------------------------------------------------------- + getExposedProperties(target: Function, transformationType: TransformationType): string[] { + return this.getExposedMetadatas(target) + .filter(metadata => { + if (!metadata.options) return true; + if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) return true; - private getMetadata(metadatas: Map>, target: Function): T[] { - const metadataFromTargetMap = metadatas.get(target); - let metadataFromTarget: T[]; - if (metadataFromTargetMap) { - metadataFromTarget = Array.from(metadataFromTargetMap.values()).filter(meta => meta.propertyName !== undefined); + if (metadata.options.toClassOnly === true) { + return ( + transformationType === TransformationType.CLASS_TO_CLASS || + transformationType === TransformationType.PLAIN_TO_CLASS + ); } - const metadataFromAncestors: T[] = []; - for (const ancestor of this.getAncestors(target)) { - const ancestorMetadataMap = metadatas.get(ancestor); - if (ancestorMetadataMap) { - const metadataFromAncestor = Array.from(ancestorMetadataMap.values()).filter(meta => meta.propertyName !== undefined); - metadataFromAncestors.push(...metadataFromAncestor); - } + if (metadata.options.toPlainOnly === true) { + return transformationType === TransformationType.CLASS_TO_PLAIN; } - return metadataFromAncestors.concat(metadataFromTarget || []); - } - private findMetadata(metadatas: Map>, target: Function, propertyName: string): T { - const metadataFromTargetMap = metadatas.get(target); - if (metadataFromTargetMap) { - const metadataFromTarget = metadataFromTargetMap.get(propertyName); - if (metadataFromTarget) { - return metadataFromTarget; - } + return true; + }) + .map(metadata => metadata.propertyName); + } + + getExcludedProperties(target: Function, transformationType: TransformationType): string[] { + return this.getExcludedMetadatas(target) + .filter(metadata => { + if (!metadata.options) return true; + if (metadata.options.toClassOnly === true && metadata.options.toPlainOnly === true) return true; + + if (metadata.options.toClassOnly === true) { + return ( + transformationType === TransformationType.CLASS_TO_CLASS || + transformationType === TransformationType.PLAIN_TO_CLASS + ); } - for (const ancestor of this.getAncestors(target)) { - const ancestorMetadataMap = metadatas.get(ancestor); - if (ancestorMetadataMap) { - const ancestorResult = ancestorMetadataMap.get(propertyName); - if (ancestorResult) { - return ancestorResult; - } - } + if (metadata.options.toPlainOnly === true) { + return transformationType === TransformationType.CLASS_TO_PLAIN; } - return undefined; + + return true; + }) + .map(metadata => metadata.propertyName); + } + + clear(): void { + this._typeMetadatas.clear(); + this._exposeMetadatas.clear(); + this._excludeMetadatas.clear(); + this._ancestorsMap.clear(); + } + + // ------------------------------------------------------------------------- + // Private Methods + // ------------------------------------------------------------------------- + + private getMetadata( + metadatas: Map>, + target: Function + ): T[] { + const metadataFromTargetMap = metadatas.get(target); + let metadataFromTarget: T[]; + if (metadataFromTargetMap) { + metadataFromTarget = Array.from(metadataFromTargetMap.values()).filter(meta => meta.propertyName !== undefined); + } + const metadataFromAncestors: T[] = []; + for (const ancestor of this.getAncestors(target)) { + const ancestorMetadataMap = metadatas.get(ancestor); + if (ancestorMetadataMap) { + const metadataFromAncestor = Array.from(ancestorMetadataMap.values()).filter( + meta => meta.propertyName !== undefined + ); + metadataFromAncestors.push(...metadataFromAncestor); + } } + return metadataFromAncestors.concat(metadataFromTarget || []); + } - private findMetadatas(metadatas: Map>, target: Function, propertyName: string): T[] { - const metadataFromTargetMap = metadatas.get(target); - let metadataFromTarget: T[]; - if (metadataFromTargetMap) { - metadataFromTarget = metadataFromTargetMap.get(propertyName); - } - const metadataFromAncestorsTarget: T[] = []; - for (const ancestor of this.getAncestors(target)) { - const ancestorMetadataMap = metadatas.get(ancestor); - if (ancestorMetadataMap) { - if (ancestorMetadataMap.has(propertyName)) { - metadataFromAncestorsTarget.push(...ancestorMetadataMap.get(propertyName)); - } - } + private findMetadata( + metadatas: Map>, + target: Function, + propertyName: string + ): T { + const metadataFromTargetMap = metadatas.get(target); + if (metadataFromTargetMap) { + const metadataFromTarget = metadataFromTargetMap.get(propertyName); + if (metadataFromTarget) { + return metadataFromTarget; + } + } + for (const ancestor of this.getAncestors(target)) { + const ancestorMetadataMap = metadatas.get(ancestor); + if (ancestorMetadataMap) { + const ancestorResult = ancestorMetadataMap.get(propertyName); + if (ancestorResult) { + return ancestorResult; } - return (metadataFromAncestorsTarget).slice().reverse().concat((metadataFromTarget || []).slice().reverse()); + } } + return undefined; + } - private getAncestors(target: Function): Function[] { - if (!target) return []; - if (!this._ancestorsMap.has(target)) { - const ancestors: Function[] = []; - for (let baseClass = Object.getPrototypeOf(target.prototype.constructor); - typeof baseClass.prototype !== "undefined"; - baseClass = Object.getPrototypeOf(baseClass.prototype.constructor)) { - ancestors.push(baseClass); - } - this._ancestorsMap.set(target, ancestors); + private findMetadatas( + metadatas: Map>, + target: Function, + propertyName: string + ): T[] { + const metadataFromTargetMap = metadatas.get(target); + let metadataFromTarget: T[]; + if (metadataFromTargetMap) { + metadataFromTarget = metadataFromTargetMap.get(propertyName); + } + const metadataFromAncestorsTarget: T[] = []; + for (const ancestor of this.getAncestors(target)) { + const ancestorMetadataMap = metadatas.get(ancestor); + if (ancestorMetadataMap) { + if (ancestorMetadataMap.has(propertyName)) { + metadataFromAncestorsTarget.push(...ancestorMetadataMap.get(propertyName)); } - return this._ancestorsMap.get(target); + } + } + return metadataFromAncestorsTarget + .slice() + .reverse() + .concat((metadataFromTarget || []).slice().reverse()); + } + + private getAncestors(target: Function): Function[] { + if (!target) return []; + if (!this._ancestorsMap.has(target)) { + const ancestors: Function[] = []; + for ( + let baseClass = Object.getPrototypeOf(target.prototype.constructor); + typeof baseClass.prototype !== 'undefined'; + baseClass = Object.getPrototypeOf(baseClass.prototype.constructor) + ) { + ancestors.push(baseClass); + } + this._ancestorsMap.set(target, ancestors); } + return this._ancestorsMap.get(target); + } } diff --git a/src/metadata/TransformMetadata.ts b/src/metadata/TransformMetadata.ts index 1e64dab65..b9a6e588d 100644 --- a/src/metadata/TransformMetadata.ts +++ b/src/metadata/TransformMetadata.ts @@ -1,12 +1,11 @@ -import {TransformOptions} from "./ExposeExcludeOptions"; -import {TransformationType} from "../TransformOperationExecutor"; +import { TransformOptions } from './ExposeExcludeOptions'; +import { TransformationType } from '../TransformOperationExecutor'; export class TransformMetadata { - - constructor(public target: Function, - public propertyName: string, - public transformFn: (value: any, obj: any, transformationType: TransformationType) => any, - public options: TransformOptions) { - } - + constructor( + public target: Function, + public propertyName: string, + public transformFn: (value: any, obj: any, transformationType: TransformationType) => any, + public options: TransformOptions + ) {} } diff --git a/src/storage.ts b/src/storage.ts index 43258687e..aa119ef2c 100644 --- a/src/storage.ts +++ b/src/storage.ts @@ -1,4 +1,4 @@ -import {MetadataStorage} from "./metadata/MetadataStorage"; +import { MetadataStorage } from './metadata/MetadataStorage'; /** * Default metadata storage is used as singleton and can be used to storage all metadatas. diff --git a/test/functional/basic-functionality.spec.ts b/test/functional/basic-functionality.spec.ts index 7f96086d9..f7ae6ee28 100644 --- a/test/functional/basic-functionality.spec.ts +++ b/test/functional/basic-functionality.spec.ts @@ -1,1794 +1,1831 @@ -import "reflect-metadata"; -import {classToClass, classToClassFromExist, classToPlain, classToPlainFromExist, plainToClass, plainToClassFromExist} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; -import {Exclude, Expose, Type} from "../../src/decorators"; -import {testForBuffer} from "../../src/TransformOperationExecutor"; - -describe("basic functionality", () => { - it("should return true if Buffer is present in environment, else false", () => { - expect(testForBuffer()).toBeTruthy(); - const bufferImp = global.Buffer; - delete global.Buffer; - expect(testForBuffer()).toBeFalsy(); - global.Buffer = bufferImp; - }); - - it("should convert instance of the given object to plain javascript object and should expose all properties since its a default behaviour", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - lastName: string; - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - - const existUser = {id: 1, age: 27}; - const plainUser2 = classToPlainFromExist(user, existUser); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - }); - - it("should exclude extraneous values if the excludeExtraneousValues option is set to true", () => { - defaultMetadataStorage.clear(); - - class User { - @Expose() id: number; - @Expose() firstName: string; - @Expose() lastName: string; - } - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - age: 12 - }; - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toHaveProperty("age"); - expect(transformedUser.id).toBeUndefined(); - - const transformedUserWithoutExtra = plainToClass(User, fromPlainUser, {excludeExtraneousValues: true}); - expect(transformedUserWithoutExtra).toBeInstanceOf(User); - expect(transformedUserWithoutExtra).not.toHaveProperty("age"); - }); - - it("should exclude all objects marked with @Exclude() decorator", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - lastName: string; - @Exclude() - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser.password).toBeUndefined(); - - const existUser = {id: 1, age: 27, password: "yayayaya"}; - const plainUser2 = classToPlainFromExist(user, existUser); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "yayayaya" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - }); - - it("should exclude all properties from object if whole class is marked with @Exclude() decorator", () => { - defaultMetadataStorage.clear(); - - @Exclude() - class User { - id: number; - firstName: string; - lastName: string; - password: string; - } - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({}); - expect(plainUser.firstName).toBeUndefined(); - expect(plainUser.lastName).toBeUndefined(); - expect(plainUser.password).toBeUndefined(); - - const existUser = {id: 1, age: 27}; - const plainUser2 = classToPlainFromExist(user, existUser); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27 - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({}); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1 - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({}); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1 - }); - }); - - it("should exclude all properties from object if whole class is marked with @Exclude() decorator, but include properties marked with @Expose() decorator", () => { - defaultMetadataStorage.clear(); - - @Exclude() - class User { - id: number; - - @Expose() - firstName: string; - - @Expose() - lastName: string; - - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser.password).toBeUndefined(); - - const existUser = {id: 1, age: 27}; - const plainUser2 = classToPlainFromExist(user, existUser); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - }); - - it("should exclude all properties from object if its defined via transformation options, but include properties marked with @Expose() decorator", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - - @Expose() - firstName: string; - - @Expose() - lastName: string; - - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user, {strategy: "excludeAll"}); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser.password).toBeUndefined(); - - const existUser = {id: 1, age: 27}; - const plainUser2 = classToPlainFromExist(user, existUser, {strategy: "excludeAll"}); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser, {strategy: "excludeAll"}); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, {strategy: "excludeAll"}); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassUser = classToClass(user, {strategy: "excludeAll"}); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, {strategy: "excludeAll"}); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - }); - - it("should expose all properties from object if its defined via transformation options, but exclude properties marked with @Exclude() decorator", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - - @Exclude() - lastName: string; - - @Exclude() - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user, {strategy: "exposeAll"}); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed" - }); - expect(plainUser.lastName).toBeUndefined(); - expect(plainUser.password).toBeUndefined(); - - const existUser = {id: 1, age: 27}; - const plainUser2 = classToPlainFromExist(user, existUser, {strategy: "exposeAll"}); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser, {strategy: "exposeAll"}); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, {strategy: "exposeAll"}); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed" - }); - - const classToClassUser = classToClass(user, {strategy: "exposeAll"}); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "Umed" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, {strategy: "exposeAll"}); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed" - }); - }); - - it("should convert values to specific types if they are set via @Type decorator", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - - @Type(type => String) - firstName: string; - - @Type(type => String) - lastName: string; - - @Type(type => Number) - password: number; - - @Type(type => Boolean) - isActive: boolean; - - @Type(type => Date) - registrationDate: Date; - - @Type(type => String) - lastVisitDate: string; - - @Type(type => Buffer) - uuidBuffer: Buffer; - - @Type(type => String) - nullableString?: null | string; - - @Type(type => Number) - nullableNumber?: null | number; - - @Type(type => Boolean) - nullableBoolean?: null | boolean; - - @Type(type => Date) - nullableDate?: null | Date; - - @Type(type => Buffer) - nullableBuffer?: null | Buffer; - } - - const date = new Date(); - const user = new User(); - const uuid = Buffer.from('1234'); - user.firstName = 321 as any; - user.lastName = 123 as any; - user.password = "123" as any; - user.isActive = "1" as any; - user.registrationDate = date.toString() as any; - user.lastVisitDate = date as any; - user.uuidBuffer = uuid as any; - user.nullableString = null as any; - user.nullableNumber = null as any; - user.nullableBoolean = null as any; - user.nullableDate = null as any; - user.nullableBuffer = null as any; - - const fromPlainUser = { - firstName: 321, - lastName: 123, - password: "123", - isActive: "1", - registrationDate: date.toString(), - lastVisitDate: date, - uuidBuffer: uuid, - nullableString: null as null | string, - nullableNumber: null as null | string, - nullableBoolean: null as null | string, - nullableDate: null as null | string, - nullableBuffer: null as null | string, - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user, {strategy: "exposeAll"}); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - uuidBuffer: uuid, - nullableString: null, - nullableNumber: null, - nullableBoolean: null, - nullableDate: null, - nullableBuffer: null - }); - - const existUser = {id: 1, age: 27}; - const plainUser2 = classToPlainFromExist(user, existUser, {strategy: "exposeAll"}); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - uuidBuffer: uuid, - nullableString: null, - nullableNumber: null, - nullableBoolean: null, - nullableDate: null, - nullableBuffer: null - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser, {strategy: "exposeAll"}); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - uuidBuffer: uuid, - nullableString: null, - nullableNumber: null, - nullableBoolean: null, - nullableDate: null, - nullableBuffer: null - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, {strategy: "exposeAll"}); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - uuidBuffer: uuid, - nullableString: null, - nullableNumber: null, - nullableBoolean: null, - nullableDate: null, - nullableBuffer: null - }); - - const classToClassUser = classToClass(user, {strategy: "exposeAll"}); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - uuidBuffer: uuid, - nullableString: null, - nullableNumber: null, - nullableBoolean: null, - nullableDate: null, - nullableBuffer: null - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, {strategy: "exposeAll"}); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - uuidBuffer: uuid, - nullableString: null, - nullableNumber: null, - nullableBoolean: null, - nullableDate: null, - nullableBuffer: null - }); - }); - - it("should transform nested objects too and make sure their decorators are used too", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - name: string; - - @Exclude() - filename: string; - - uploadDate: Date; - } - - class User { - firstName: string; - lastName: string; - - @Exclude() - password: string; - - photo: Photo; // type should be automatically guessed - } - - const photo = new Photo(); - photo.id = 1; - photo.name = "Me"; - photo.filename = "iam.jpg"; - photo.uploadDate = new Date(); - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = photo; - - const plainUser: any = classToPlain(user, {strategy: "exposeAll"}); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser.photo).not.toBeInstanceOf(Photo); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - uploadDate: photo.uploadDate - } - }); - expect(plainUser.password).toBeUndefined(); - expect(plainUser.photo.filename).toBeUndefined(); - expect(plainUser.photo.uploadDate).toEqual(photo.uploadDate); - expect(plainUser.photo.uploadDate).not.toBe(photo.uploadDate); - - const existUser = {id: 1, age: 27, photo: {id: 2, description: "photo"}}; - const plainUser2: any = classToPlainFromExist(user, existUser, {strategy: "exposeAll"}); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2.photo).not.toBeInstanceOf(Photo); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - uploadDate: photo.uploadDate, - description: "photo" - } - }); - expect(plainUser2).toEqual(existUser); - expect(plainUser2.password).toBeUndefined(); - expect(plainUser2.photo.filename).toBeUndefined(); - expect(plainUser2.photo.uploadDate).toEqual(photo.uploadDate); - expect(plainUser2.photo.uploadDate).not.toBe(photo.uploadDate); - }); - - it("should transform nested objects too and make sure given type is used instead of automatically guessed one", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - name: string; - - @Exclude() - filename: string; - } - - class ExtendedPhoto implements Photo { - id: number; - - @Exclude() - name: string; - - filename: string; - } - - class User { - id: number; - firstName: string; - lastName: string; - - @Exclude() - password: string; - - @Type(type => ExtendedPhoto) // force specific type - photo: Photo; - } - - const photo = new Photo(); - photo.id = 1; - photo.name = "Me"; - photo.filename = "iam.jpg"; - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = photo; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "iam.jpg" - } - }); - expect(plainUser.password).toBeUndefined(); - expect(plainUser.photo.name).toBeUndefined(); - }); - - it("should convert given plain object to class instance object", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - name: string; - - @Exclude() - filename: string; - - metadata: string; - uploadDate: Date; - } - - class User { - id: number; - firstName: string; - lastName: string; - - @Exclude() - password: string; - - @Type(type => Photo) - photo: Photo; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = new Photo(); - user.photo.id = 1; - user.photo.name = "Me"; - user.photo.filename = "iam.jpg"; - user.photo.uploadDate = new Date(); - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - name: "Me", - filename: "iam.jpg", - uploadDate: new Date(), - } - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - const fromExistPhoto = new Photo(); - fromExistPhoto.metadata = "taken by Camera"; - fromExistUser.photo = fromExistPhoto; - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser.photo).toBeInstanceOf(Photo); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - uploadDate: fromPlainUser.photo.uploadDate - } - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toEqual(fromExistUser); - expect(fromExistTransformedUser.photo).toEqual(fromExistPhoto); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - metadata: "taken by Camera", - uploadDate: fromPlainUser.photo.uploadDate - } - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser.photo).toBeInstanceOf(Photo); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).not.toEqual(user.photo); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - uploadDate: user.photo.uploadDate - } - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).not.toEqual(user.photo); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - metadata: "taken by Camera", - uploadDate: user.photo.uploadDate - } - }); - }); - - it("should expose only properties that match given group", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - - @Expose({ - groups: ["user", "guest"] - }) - filename: string; - - @Expose({ - groups: ["admin"] - }) - status: number; - - metadata: string; - } - - class User { - id: number; - firstName: string; - - @Expose({ - groups: ["user", "guest"] - }) - lastName: string; - - @Expose({ - groups: ["user"] - }) - password: string; - - @Expose({ - groups: ["admin"] - }) - isActive: boolean; - - @Type(type => Photo) - photo: Photo; - - @Expose({ - groups: ["admin"] - }) - @Type(type => Photo) - photos: Photo[]; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.isActive = false; - user.photo = new Photo(); - user.photo.id = 1; - user.photo.filename = "myphoto.jpg"; - user.photo.status = 1; - user.photos = [user.photo]; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - isActive: false, - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1, - }] - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - fromExistUser.photo = new Photo(); - fromExistUser.photo.metadata = "taken by Camera"; - - const plainUser1: any = classToPlain(user); - expect(plainUser1).not.toBeInstanceOf(User); - expect(plainUser1).toEqual({ - firstName: "Umed", - photo: { - id: 1 - } - }); - expect(plainUser1.lastName).toBeUndefined(); - expect(plainUser1.password).toBeUndefined(); - expect(plainUser1.isActive).toBeUndefined(); - - const plainUser2: any = classToPlain(user, {groups: ["user"]}); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - expect(plainUser2.isActive).toBeUndefined(); - - const transformedUser2 = plainToClass(User, fromPlainUser, {groups: ["user"]}); - expect(transformedUser2).toBeInstanceOf(User); - expect(transformedUser2.photo).toBeInstanceOf(Photo); - expect(transformedUser2).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, {groups: ["user"]}); - expect(fromExistTransformedUser).toEqual(fromExistUser); - expect(fromExistTransformedUser.photo).toEqual(fromExistUser.photo); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - metadata: "taken by Camera", - filename: "myphoto.jpg" - } - }); - - const classToClassUser = classToClass(user, {groups: ["user"]}); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser.photo).toBeInstanceOf(Photo); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).not.toEqual(user.photo); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, {groups: ["user"]}); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).not.toEqual(user.photo); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - metadata: "taken by Camera", - filename: "myphoto.jpg" - } - }); - - const plainUser3: any = classToPlain(user, {groups: ["guest"]}); - expect(plainUser3).not.toBeInstanceOf(User); - expect(plainUser3).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - expect(plainUser3.password).toBeUndefined(); - expect(plainUser3.isActive).toBeUndefined(); - - const transformedUser3 = plainToClass(User, fromPlainUser, {groups: ["guest"]}); - expect(transformedUser3).toBeInstanceOf(User); - expect(transformedUser3.photo).toBeInstanceOf(Photo); - expect(transformedUser3).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const plainUser4: any = classToPlain(user, {groups: ["admin"]}); - expect(plainUser4).not.toBeInstanceOf(User); - expect(plainUser4).toEqual({ - firstName: "Umed", - isActive: false, - photo: { - id: 1, - status: 1 - }, - photos: [{ - id: 1, - status: 1 - }] - }); - expect(plainUser4.lastName).toBeUndefined(); - expect(plainUser4.password).toBeUndefined(); - - const transformedUser4 = plainToClass(User, fromPlainUser, {groups: ["admin"]}); - expect(transformedUser4).toBeInstanceOf(User); - expect(transformedUser4.photo).toBeInstanceOf(Photo); - expect(transformedUser4.photos[0]).toBeInstanceOf(Photo); - expect(transformedUser4).toEqual({ - firstName: "Umed", - isActive: false, - photo: { - id: 1, - status: 1 - }, - photos: [{ - id: 1, - status: 1 - }] - }); - - const plainUser5: any = classToPlain(user, {groups: ["admin", "user"]}); - expect(plainUser5).not.toBeInstanceOf(User); - expect(plainUser5).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - isActive: false, - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1 - }] - }); - - const transformedUser5 = plainToClass(User, fromPlainUser, {groups: ["admin", "user"]}); - expect(transformedUser5).toBeInstanceOf(User); - expect(transformedUser5).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - isActive: false, - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1 - }] - }); - }); - - it("should expose only properties that match given version", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - - @Expose({ - since: 1.5, - until: 2 - }) - filename: string; - - @Expose({ - since: 2 - }) - status: number; - } - - class User { - @Expose({ - since: 1, - until: 2 - }) - firstName: string; - - @Expose({ - since: 0.5 - }) - lastName: string; - - @Exclude() - password: string; - - @Type(type => Photo) - photo: Photo; - - @Expose({ - since: 3 - }) - @Type(type => Photo) - photos: Photo[]; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = new Photo(); - user.photo.id = 1; - user.photo.filename = "myphoto.jpg"; - user.photo.status = 1; - user.photos = [user.photo]; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1, - }] - }; - - const plainUser1: any = classToPlain(user); - expect(plainUser1).not.toBeInstanceOf(User); - expect(plainUser1).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1 - }] - }); - - const transformedUser1 = plainToClass(User, fromPlainUser); - expect(transformedUser1).toBeInstanceOf(User); - expect(transformedUser1.photo).toBeInstanceOf(Photo); - expect(transformedUser1.photos[0]).toBeInstanceOf(Photo); - expect(transformedUser1).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1 - }] - }); - - const plainUser2: any = classToPlain(user, {version: 0.3}); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - photo: { - id: 1 - } - }); - - const transformedUser2 = plainToClass(User, fromPlainUser, {version: 0.3}); - expect(transformedUser2).toBeInstanceOf(User); - expect(transformedUser2.photo).toBeInstanceOf(Photo); - expect(transformedUser2).toEqual({ - photo: { - id: 1 - } - }); - - const plainUser3: any = classToPlain(user, {version: 0.5}); - expect(plainUser3).not.toBeInstanceOf(User); - expect(plainUser3).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1 - } - }); - - const transformedUser3 = plainToClass(User, fromPlainUser, {version: 0.5}); - expect(transformedUser3).toBeInstanceOf(User); - expect(transformedUser3.photo).toBeInstanceOf(Photo); - expect(transformedUser3).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1 - } - }); - - const plainUser4: any = classToPlain(user, {version: 1}); - expect(plainUser4).not.toBeInstanceOf(User); - expect(plainUser4).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1 - } - }); - - const transformedUser4 = plainToClass(User, fromPlainUser, {version: 1}); - expect(transformedUser4).toBeInstanceOf(User); - expect(transformedUser4.photo).toBeInstanceOf(Photo); - expect(transformedUser4).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1 - } - }); - - const plainUser5: any = classToPlain(user, {version: 1.5}); - expect(plainUser5).not.toBeInstanceOf(User); - expect(plainUser5).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const transformedUser5 = plainToClass(User, fromPlainUser, {version: 1.5}); - expect(transformedUser5).toBeInstanceOf(User); - expect(transformedUser5.photo).toBeInstanceOf(Photo); - expect(transformedUser5).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const plainUser6: any = classToPlain(user, {version: 2}); - expect(plainUser6).not.toBeInstanceOf(User); - expect(plainUser6).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1, - status: 1 - } - }); - - const transformedUser6 = plainToClass(User, fromPlainUser, {version: 2}); - expect(transformedUser6).toBeInstanceOf(User); - expect(transformedUser6.photo).toBeInstanceOf(Photo); - expect(transformedUser6).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1, - status: 1 - } - }); - - const plainUser7: any = classToPlain(user, {version: 3}); - expect(plainUser7).not.toBeInstanceOf(User); - expect(plainUser7).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1, - status: 1 - }, - photos: [{ - id: 1, - status: 1 - }] - }); - - const transformedUser7 = plainToClass(User, fromPlainUser, {version: 3}); - expect(transformedUser7).toBeInstanceOf(User); - expect(transformedUser7.photo).toBeInstanceOf(Photo); - expect(transformedUser7.photos[0]).toBeInstanceOf(Photo); - expect(transformedUser7).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1, - status: 1 - }, - photos: [{ - id: 1, - status: 1 - }] - }); - - }); - - it("should expose method and accessors that have @Expose()", () => { - defaultMetadataStorage.clear(); - - class User { - firstName: string; - lastName: string; - - @Exclude() - password: string; - - @Expose() - get name(): string { - return this.firstName + " " + this.lastName; - } - - @Expose() - getName(): string { - return this.firstName + " " + this.lastName; - } - - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - name: "Umed Khudoiberdiev", - getName: "Umed Khudoiberdiev" - }); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - const likeUser = new User(); - likeUser.firstName = "Umed"; - likeUser.lastName = "Khudoiberdiev"; - expect(transformedUser).toEqual(likeUser); - }); - - it("should expose with alternative name if its given", () => { - defaultMetadataStorage.clear(); - - class User { - @Expose({name: "myName"}) - firstName: string; - - @Expose({name: "secondName"}) - lastName: string; - - @Exclude() - password: string; - - @Expose() - get name(): string { - return this.firstName + " " + this.lastName; - } - - @Expose({name: "fullName"}) - getName(): string { - return this.firstName + " " + this.lastName; - } - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - myName: "Umed", - secondName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - myName: "Umed", - secondName: "Khudoiberdiev", - name: "Umed Khudoiberdiev", - fullName: "Umed Khudoiberdiev" - }); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - const likeUser = new User(); - likeUser.firstName = "Umed"; - likeUser.lastName = "Khudoiberdiev"; - expect(transformedUser).toEqual(likeUser); - }); - - it("should exclude all prefixed properties if prefix is given", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - $filename: string; - status: number; - } - - class User { - $system: string; - _firstName: string; - _lastName: string; - - @Exclude() - password: string; - - @Type(() => Photo) - photo: Photo; - - @Expose() - get name(): string { - return this._firstName + " " + this._lastName; - } - } - - const user = new User(); - user.$system = "@#$%^&*token(*&^%$#@!"; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = new Photo(); - user.photo.id = 1; - user.photo.$filename = "myphoto.jpg"; - user.photo.status = 1; - - const fromPlainUser = { - $system: "@#$%^&*token(*&^%$#@!", - _firstName: "Khudoiberdiev", - _lastName: "imnosuperman", - password: "imnosuperman", - photo: { - id: 1, - $filename: "myphoto.jpg", - status: 1, - } - }; - - const plainUser: any = classToPlain(user, {excludePrefixes: ["_", "$"]}); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - name: "Umed Khudoiberdiev", - photo: { - id: 1, - status: 1 - } - }); - - const transformedUser = plainToClass(User, fromPlainUser, {excludePrefixes: ["_", "$"]}); - expect(transformedUser).toBeInstanceOf(User); - const likeUser = new User(); - likeUser.photo = new Photo(); - likeUser.photo.id = 1; - likeUser.photo.status = 1; - expect(transformedUser).toEqual(likeUser); - }); - - it("should transform array", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - lastName: string; - - @Exclude() - password: string; - - @Expose() - get name(): string { - return this.firstName + " " + this.lastName; - } - } - - const user1 = new User(); - user1.firstName = "Umed"; - user1.lastName = "Khudoiberdiev"; - user1.password = "imnosuperman"; - - const user2 = new User(); - user2.firstName = "Dima"; - user2.lastName = "Zotov"; - user2.password = "imnomesser"; - - const users = [user1, user2]; - - const plainUsers: any = classToPlain(users); - expect(plainUsers).toEqual([{ - firstName: "Umed", - lastName: "Khudoiberdiev", - name: "Umed Khudoiberdiev" - }, { - firstName: "Dima", - lastName: "Zotov", - name: "Dima Zotov" - }]); - - const fromPlainUsers = [{ - firstName: "Umed", - lastName: "Khudoiberdiev", - name: "Umed Khudoiberdiev" - }, { - firstName: "Dima", - lastName: "Zotov", - name: "Dima Zotov" - }]; - - const existUsers = [{id: 1, age: 27}, {id: 2, age: 30}]; - const plainUser2 = classToPlainFromExist(users, existUsers); - expect(plainUser2).toEqual([{ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev", - name: "Umed Khudoiberdiev" - }, { - id: 2, - age: 30, - firstName: "Dima", - lastName: "Zotov", - name: "Dima Zotov" - }]); - - const transformedUser = plainToClass(User, fromPlainUsers); - - expect(transformedUser[0]).toBeInstanceOf(User); - expect(transformedUser[1]).toBeInstanceOf(User); - const likeUser1 = new User(); - likeUser1.firstName = "Umed"; - likeUser1.lastName = "Khudoiberdiev"; - - const likeUser2 = new User(); - likeUser2.firstName = "Dima"; - likeUser2.lastName = "Zotov"; - expect(transformedUser).toEqual([likeUser1, likeUser2]); - - const classToClassUsers = classToClass(users); - expect(classToClassUsers[0]).toBeInstanceOf(User); - expect(classToClassUsers[1]).toBeInstanceOf(User); - expect(classToClassUsers[0]).not.toEqual(user1); - expect(classToClassUsers[1]).not.toEqual(user1); - - const classUserLike1 = new User(); - classUserLike1.firstName = "Umed"; - classUserLike1.lastName = "Khudoiberdiev"; - - const classUserLike2 = new User(); - classUserLike2.firstName = "Dima"; - classUserLike2.lastName = "Zotov"; - - expect(classToClassUsers).toEqual([classUserLike1, classUserLike2]); - - const fromExistUser1 = new User(); - fromExistUser1.id = 1; - - const fromExistUser2 = new User(); - fromExistUser2.id = 2; - - const fromExistUsers = [fromExistUser1, fromExistUser2]; - - const classToClassFromExistUser = classToClassFromExist(users, fromExistUsers); - expect(classToClassFromExistUser[0]).toBeInstanceOf(User); - expect(classToClassFromExistUser[1]).toBeInstanceOf(User); - expect(classToClassFromExistUser[0]).not.toEqual(user1); - expect(classToClassFromExistUser[1]).not.toEqual(user1); - expect(classToClassFromExistUser).toEqual(fromExistUsers); - - const fromExistUserLike1 = new User(); - fromExistUserLike1.id = 1; - fromExistUserLike1.firstName = "Umed"; - fromExistUserLike1.lastName = "Khudoiberdiev"; - - const fromExistUserLike2 = new User(); - fromExistUserLike2.id = 2; - fromExistUserLike2.firstName = "Dima"; - fromExistUserLike2.lastName = "Zotov"; - - expect(classToClassFromExistUser).toEqual([fromExistUserLike1, fromExistUserLike2]); - }); - - it("should transform objects with null prototype", () => { - class TestClass { - prop: string; - } - - const obj = Object.create(null); - obj.a = "JS FTW"; - - const transformedClass = plainToClass(TestClass, obj); - expect(transformedClass).toBeInstanceOf(TestClass); - }); - - it('should not pollute the prototype with a `__proto__` property',() => { - const object = JSON.parse('{"__proto__": { "admin": true }}'); - const plainObject = {}; - classToPlainFromExist(object, plainObject); - expect((plainObject as any).admin).toEqual(undefined); - }); - - it('should not pollute the prototype with a `constructor.prototype` property', () => { - const object = JSON.parse('{"constructor": { "prototype": { "admin": true }}}'); - const plainObject = {}; - classToPlainFromExist(object, plainObject); - expect((plainObject as any).admin).toEqual(undefined); +import 'reflect-metadata'; +import { + classToClass, + classToClassFromExist, + classToPlain, + classToPlainFromExist, + plainToClass, + plainToClassFromExist, +} from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { Exclude, Expose, Type } from '../../src/decorators'; +import { testForBuffer } from '../../src/TransformOperationExecutor'; + +describe('basic functionality', () => { + it('should return true if Buffer is present in environment, else false', () => { + expect(testForBuffer()).toBeTruthy(); + const bufferImp = global.Buffer; + delete global.Buffer; + expect(testForBuffer()).toBeFalsy(); + global.Buffer = bufferImp; + }); + + it('should convert instance of the given object to plain javascript object and should expose all properties since its a default behaviour', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + lastName: string; + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', }); - it("should default union types where the plain type is an array to an array result", () => { - class User { - name: string; - } + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).toEqual(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + }); + + it('should exclude extraneous values if the excludeExtraneousValues option is set to true', () => { + defaultMetadataStorage.clear(); + + class User { + @Expose() id: number; + @Expose() firstName: string; + @Expose() lastName: string; + } + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + age: 12, + }; + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toHaveProperty('age'); + expect(transformedUser.id).toBeUndefined(); + + const transformedUserWithoutExtra = plainToClass(User, fromPlainUser, { excludeExtraneousValues: true }); + expect(transformedUserWithoutExtra).toBeInstanceOf(User); + expect(transformedUserWithoutExtra).not.toHaveProperty('age'); + }); + + it('should exclude all objects marked with @Exclude() decorator', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + lastName: string; + @Exclude() + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27, password: 'yayayaya' }; + const plainUser2 = classToPlainFromExist(user, existUser); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'yayayaya', + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + }); + + it('should exclude all properties from object if whole class is marked with @Exclude() decorator', () => { + defaultMetadataStorage.clear(); + + @Exclude() + class User { + id: number; + firstName: string; + lastName: string; + password: string; + } + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({}); + expect(plainUser.firstName).toBeUndefined(); + expect(plainUser.lastName).toBeUndefined(); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({}); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({}); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + }); + }); + + it('should exclude all properties from object if whole class is marked with @Exclude() decorator, but include properties marked with @Expose() decorator', () => { + defaultMetadataStorage.clear(); + + @Exclude() + class User { + id: number; + + @Expose() + firstName: string; + + @Expose() + lastName: string; + + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + }); + + it('should exclude all properties from object if its defined via transformation options, but include properties marked with @Expose() decorator', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + + @Expose() + firstName: string; + + @Expose() + lastName: string; + + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user, { strategy: 'excludeAll' }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser, { strategy: 'excludeAll' }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser, { strategy: 'excludeAll' }); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: 'excludeAll' }); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassUser = classToClass(user, { strategy: 'excludeAll' }); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: 'excludeAll' }); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + }); + + it('should expose all properties from object if its defined via transformation options, but exclude properties marked with @Exclude() decorator', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + + @Exclude() + lastName: string; + + @Exclude() + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user, { strategy: 'exposeAll' }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + }); + expect(plainUser.lastName).toBeUndefined(); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser, { strategy: 'exposeAll' }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + }); + expect(plainUser2).toEqual(existUser); - class TestClass { - @Type(() => User) - usersDefined: User[] | undefined; + const transformedUser = plainToClass(User, fromPlainUser, { strategy: 'exposeAll' }); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: 'exposeAll' }); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + }); + + const classToClassUser = classToClass(user, { strategy: 'exposeAll' }); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: 'exposeAll' }); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + }); + }); + + it('should convert values to specific types if they are set via @Type decorator', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + + @Type(type => String) + firstName: string; + + @Type(type => String) + lastName: string; + + @Type(type => Number) + password: number; + + @Type(type => Boolean) + isActive: boolean; + + @Type(type => Date) + registrationDate: Date; + + @Type(type => String) + lastVisitDate: string; + + @Type(type => Buffer) + uuidBuffer: Buffer; + + @Type(type => String) + nullableString?: null | string; + + @Type(type => Number) + nullableNumber?: null | number; + + @Type(type => Boolean) + nullableBoolean?: null | boolean; + + @Type(type => Date) + nullableDate?: null | Date; + + @Type(type => Buffer) + nullableBuffer?: null | Buffer; + } + + const date = new Date(); + const user = new User(); + const uuid = Buffer.from('1234'); + user.firstName = 321 as any; + user.lastName = 123 as any; + user.password = '123' as any; + user.isActive = '1' as any; + user.registrationDate = date.toString() as any; + user.lastVisitDate = date as any; + user.uuidBuffer = uuid as any; + user.nullableString = null as any; + user.nullableNumber = null as any; + user.nullableBoolean = null as any; + user.nullableDate = null as any; + user.nullableBuffer = null as any; + + const fromPlainUser = { + firstName: 321, + lastName: 123, + password: '123', + isActive: '1', + registrationDate: date.toString(), + lastVisitDate: date, + uuidBuffer: uuid, + nullableString: null as null | string, + nullableNumber: null as null | string, + nullableBoolean: null as null | string, + nullableDate: null as null | string, + nullableBuffer: null as null | string, + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user, { strategy: 'exposeAll' }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + uuidBuffer: uuid, + nullableString: null, + nullableNumber: null, + nullableBoolean: null, + nullableDate: null, + nullableBuffer: null, + }); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser, { strategy: 'exposeAll' }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + uuidBuffer: uuid, + nullableString: null, + nullableNumber: null, + nullableBoolean: null, + nullableDate: null, + nullableBuffer: null, + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser, { strategy: 'exposeAll' }); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + uuidBuffer: uuid, + nullableString: null, + nullableNumber: null, + nullableBoolean: null, + nullableDate: null, + nullableBuffer: null, + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: 'exposeAll' }); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + uuidBuffer: uuid, + nullableString: null, + nullableNumber: null, + nullableBoolean: null, + nullableDate: null, + nullableBuffer: null, + }); + + const classToClassUser = classToClass(user, { strategy: 'exposeAll' }); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + uuidBuffer: uuid, + nullableString: null, + nullableNumber: null, + nullableBoolean: null, + nullableDate: null, + nullableBuffer: null, + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: 'exposeAll' }); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + uuidBuffer: uuid, + nullableString: null, + nullableNumber: null, + nullableBoolean: null, + nullableDate: null, + nullableBuffer: null, + }); + }); + + it('should transform nested objects too and make sure their decorators are used too', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + name: string; + + @Exclude() + filename: string; + + uploadDate: Date; + } + + class User { + firstName: string; + lastName: string; + + @Exclude() + password: string; + + photo: Photo; // type should be automatically guessed + } + + const photo = new Photo(); + photo.id = 1; + photo.name = 'Me'; + photo.filename = 'iam.jpg'; + photo.uploadDate = new Date(); + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = photo; + + const plainUser: any = classToPlain(user, { strategy: 'exposeAll' }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser.photo).not.toBeInstanceOf(Photo); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + uploadDate: photo.uploadDate, + }, + }); + expect(plainUser.password).toBeUndefined(); + expect(plainUser.photo.filename).toBeUndefined(); + expect(plainUser.photo.uploadDate).toEqual(photo.uploadDate); + expect(plainUser.photo.uploadDate).not.toBe(photo.uploadDate); + + const existUser = { id: 1, age: 27, photo: { id: 2, description: 'photo' } }; + const plainUser2: any = classToPlainFromExist(user, existUser, { strategy: 'exposeAll' }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2.photo).not.toBeInstanceOf(Photo); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + uploadDate: photo.uploadDate, + description: 'photo', + }, + }); + expect(plainUser2).toEqual(existUser); + expect(plainUser2.password).toBeUndefined(); + expect(plainUser2.photo.filename).toBeUndefined(); + expect(plainUser2.photo.uploadDate).toEqual(photo.uploadDate); + expect(plainUser2.photo.uploadDate).not.toBe(photo.uploadDate); + }); + + it('should transform nested objects too and make sure given type is used instead of automatically guessed one', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + name: string; + + @Exclude() + filename: string; + } + + class ExtendedPhoto implements Photo { + id: number; + + @Exclude() + name: string; + + filename: string; + } + + class User { + id: number; + firstName: string; + lastName: string; + + @Exclude() + password: string; + + @Type(type => ExtendedPhoto) // force specific type + photo: Photo; + } + + const photo = new Photo(); + photo.id = 1; + photo.name = 'Me'; + photo.filename = 'iam.jpg'; + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = photo; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'iam.jpg', + }, + }); + expect(plainUser.password).toBeUndefined(); + expect(plainUser.photo.name).toBeUndefined(); + }); + + it('should convert given plain object to class instance object', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + name: string; + + @Exclude() + filename: string; + + metadata: string; + uploadDate: Date; + } + + class User { + id: number; + firstName: string; + lastName: string; + + @Exclude() + password: string; + + @Type(type => Photo) + photo: Photo; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = new Photo(); + user.photo.id = 1; + user.photo.name = 'Me'; + user.photo.filename = 'iam.jpg'; + user.photo.uploadDate = new Date(); + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + name: 'Me', + filename: 'iam.jpg', + uploadDate: new Date(), + }, + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + const fromExistPhoto = new Photo(); + fromExistPhoto.metadata = 'taken by Camera'; + fromExistUser.photo = fromExistPhoto; + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser.photo).toBeInstanceOf(Photo); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + uploadDate: fromPlainUser.photo.uploadDate, + }, + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toEqual(fromExistUser); + expect(fromExistTransformedUser.photo).toEqual(fromExistPhoto); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + metadata: 'taken by Camera', + uploadDate: fromPlainUser.photo.uploadDate, + }, + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser.photo).toBeInstanceOf(Photo); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).not.toEqual(user.photo); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + uploadDate: user.photo.uploadDate, + }, + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).not.toEqual(user.photo); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + metadata: 'taken by Camera', + uploadDate: user.photo.uploadDate, + }, + }); + }); + + it('should expose only properties that match given group', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + + @Expose({ + groups: ['user', 'guest'], + }) + filename: string; + + @Expose({ + groups: ['admin'], + }) + status: number; + + metadata: string; + } + + class User { + id: number; + firstName: string; + + @Expose({ + groups: ['user', 'guest'], + }) + lastName: string; + + @Expose({ + groups: ['user'], + }) + password: string; + + @Expose({ + groups: ['admin'], + }) + isActive: boolean; + + @Type(type => Photo) + photo: Photo; + + @Expose({ + groups: ['admin'], + }) + @Type(type => Photo) + photos: Photo[]; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.isActive = false; + user.photo = new Photo(); + user.photo.id = 1; + user.photo.filename = 'myphoto.jpg'; + user.photo.status = 1; + user.photos = [user.photo]; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + isActive: false, + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + fromExistUser.photo = new Photo(); + fromExistUser.photo.metadata = 'taken by Camera'; + + const plainUser1: any = classToPlain(user); + expect(plainUser1).not.toBeInstanceOf(User); + expect(plainUser1).toEqual({ + firstName: 'Umed', + photo: { + id: 1, + }, + }); + expect(plainUser1.lastName).toBeUndefined(); + expect(plainUser1.password).toBeUndefined(); + expect(plainUser1.isActive).toBeUndefined(); + + const plainUser2: any = classToPlain(user, { groups: ['user'] }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + expect(plainUser2.isActive).toBeUndefined(); + + const transformedUser2 = plainToClass(User, fromPlainUser, { groups: ['user'] }); + expect(transformedUser2).toBeInstanceOf(User); + expect(transformedUser2.photo).toBeInstanceOf(Photo); + expect(transformedUser2).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { groups: ['user'] }); + expect(fromExistTransformedUser).toEqual(fromExistUser); + expect(fromExistTransformedUser.photo).toEqual(fromExistUser.photo); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + metadata: 'taken by Camera', + filename: 'myphoto.jpg', + }, + }); + + const classToClassUser = classToClass(user, { groups: ['user'] }); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser.photo).toBeInstanceOf(Photo); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).not.toEqual(user.photo); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { groups: ['user'] }); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).not.toEqual(user.photo); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + metadata: 'taken by Camera', + filename: 'myphoto.jpg', + }, + }); - @Type(() => User) - usersUndefined: User[] | undefined; - } + const plainUser3: any = classToPlain(user, { groups: ['guest'] }); + expect(plainUser3).not.toBeInstanceOf(User); + expect(plainUser3).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + expect(plainUser3.password).toBeUndefined(); + expect(plainUser3.isActive).toBeUndefined(); + + const transformedUser3 = plainToClass(User, fromPlainUser, { groups: ['guest'] }); + expect(transformedUser3).toBeInstanceOf(User); + expect(transformedUser3.photo).toBeInstanceOf(Photo); + expect(transformedUser3).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + + const plainUser4: any = classToPlain(user, { groups: ['admin'] }); + expect(plainUser4).not.toBeInstanceOf(User); + expect(plainUser4).toEqual({ + firstName: 'Umed', + isActive: false, + photo: { + id: 1, + status: 1, + }, + photos: [ + { + id: 1, + status: 1, + }, + ], + }); + expect(plainUser4.lastName).toBeUndefined(); + expect(plainUser4.password).toBeUndefined(); + + const transformedUser4 = plainToClass(User, fromPlainUser, { groups: ['admin'] }); + expect(transformedUser4).toBeInstanceOf(User); + expect(transformedUser4.photo).toBeInstanceOf(Photo); + expect(transformedUser4.photos[0]).toBeInstanceOf(Photo); + expect(transformedUser4).toEqual({ + firstName: 'Umed', + isActive: false, + photo: { + id: 1, + status: 1, + }, + photos: [ + { + id: 1, + status: 1, + }, + ], + }); + + const plainUser5: any = classToPlain(user, { groups: ['admin', 'user'] }); + expect(plainUser5).not.toBeInstanceOf(User); + expect(plainUser5).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + isActive: false, + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }); + + const transformedUser5 = plainToClass(User, fromPlainUser, { groups: ['admin', 'user'] }); + expect(transformedUser5).toBeInstanceOf(User); + expect(transformedUser5).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + isActive: false, + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }); + }); + + it('should expose only properties that match given version', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + + @Expose({ + since: 1.5, + until: 2, + }) + filename: string; + + @Expose({ + since: 2, + }) + status: number; + } + + class User { + @Expose({ + since: 1, + until: 2, + }) + firstName: string; + + @Expose({ + since: 0.5, + }) + lastName: string; + + @Exclude() + password: string; + + @Type(type => Photo) + photo: Photo; + + @Expose({ + since: 3, + }) + @Type(type => Photo) + photos: Photo[]; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = new Photo(); + user.photo.id = 1; + user.photo.filename = 'myphoto.jpg'; + user.photo.status = 1; + user.photos = [user.photo]; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }; + + const plainUser1: any = classToPlain(user); + expect(plainUser1).not.toBeInstanceOf(User); + expect(plainUser1).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }); + + const transformedUser1 = plainToClass(User, fromPlainUser); + expect(transformedUser1).toBeInstanceOf(User); + expect(transformedUser1.photo).toBeInstanceOf(Photo); + expect(transformedUser1.photos[0]).toBeInstanceOf(Photo); + expect(transformedUser1).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }); + + const plainUser2: any = classToPlain(user, { version: 0.3 }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + photo: { + id: 1, + }, + }); + + const transformedUser2 = plainToClass(User, fromPlainUser, { version: 0.3 }); + expect(transformedUser2).toBeInstanceOf(User); + expect(transformedUser2.photo).toBeInstanceOf(Photo); + expect(transformedUser2).toEqual({ + photo: { + id: 1, + }, + }); + + const plainUser3: any = classToPlain(user, { version: 0.5 }); + expect(plainUser3).not.toBeInstanceOf(User); + expect(plainUser3).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + }, + }); - const obj = Object.create(null); - obj.usersDefined = [{name: "a-name"}]; - obj.usersUndefined = undefined; + const transformedUser3 = plainToClass(User, fromPlainUser, { version: 0.5 }); + expect(transformedUser3).toBeInstanceOf(User); + expect(transformedUser3.photo).toBeInstanceOf(Photo); + expect(transformedUser3).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + }, + }); + + const plainUser4: any = classToPlain(user, { version: 1 }); + expect(plainUser4).not.toBeInstanceOf(User); + expect(plainUser4).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + }, + }); + + const transformedUser4 = plainToClass(User, fromPlainUser, { version: 1 }); + expect(transformedUser4).toBeInstanceOf(User); + expect(transformedUser4.photo).toBeInstanceOf(Photo); + expect(transformedUser4).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + }, + }); + + const plainUser5: any = classToPlain(user, { version: 1.5 }); + expect(plainUser5).not.toBeInstanceOf(User); + expect(plainUser5).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + + const transformedUser5 = plainToClass(User, fromPlainUser, { version: 1.5 }); + expect(transformedUser5).toBeInstanceOf(User); + expect(transformedUser5.photo).toBeInstanceOf(Photo); + expect(transformedUser5).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); - const transformedClass = plainToClass(TestClass, obj as Record); + const plainUser6: any = classToPlain(user, { version: 2 }); + expect(plainUser6).not.toBeInstanceOf(User); + expect(plainUser6).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + }); - expect(transformedClass).toBeInstanceOf(TestClass); + const transformedUser6 = plainToClass(User, fromPlainUser, { version: 2 }); + expect(transformedUser6).toBeInstanceOf(User); + expect(transformedUser6.photo).toBeInstanceOf(Photo); + expect(transformedUser6).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + }); - expect(transformedClass.usersDefined).toBeInstanceOf(Array); - expect(transformedClass.usersDefined.length).toEqual(1); - expect(transformedClass.usersDefined[0]).toBeInstanceOf(User); - expect(transformedClass.usersDefined[0].name).toEqual("a-name"); + const plainUser7: any = classToPlain(user, { version: 3 }); + expect(plainUser7).not.toBeInstanceOf(User); + expect(plainUser7).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + photos: [ + { + id: 1, + status: 1, + }, + ], + }); - expect(transformedClass.usersUndefined).toBeUndefined(); + const transformedUser7 = plainToClass(User, fromPlainUser, { version: 3 }); + expect(transformedUser7).toBeInstanceOf(User); + expect(transformedUser7.photo).toBeInstanceOf(Photo); + expect(transformedUser7.photos[0]).toBeInstanceOf(Photo); + expect(transformedUser7).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + photos: [ + { + id: 1, + status: 1, + }, + ], }); + }); + + it('should expose method and accessors that have @Expose()', () => { + defaultMetadataStorage.clear(); + + class User { + firstName: string; + lastName: string; + + @Exclude() + password: string; + + @Expose() + get name(): string { + return this.firstName + ' ' + this.lastName; + } + + @Expose() + getName(): string { + return this.firstName + ' ' + this.lastName; + } + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + getName: 'Umed Khudoiberdiev', + }); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + const likeUser = new User(); + likeUser.firstName = 'Umed'; + likeUser.lastName = 'Khudoiberdiev'; + expect(transformedUser).toEqual(likeUser); + }); + + it('should expose with alternative name if its given', () => { + defaultMetadataStorage.clear(); + + class User { + @Expose({ name: 'myName' }) + firstName: string; + + @Expose({ name: 'secondName' }) + lastName: string; + + @Exclude() + password: string; + + @Expose() + get name(): string { + return this.firstName + ' ' + this.lastName; + } + + @Expose({ name: 'fullName' }) + getName(): string { + return this.firstName + ' ' + this.lastName; + } + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + myName: 'Umed', + secondName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + myName: 'Umed', + secondName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + fullName: 'Umed Khudoiberdiev', + }); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + const likeUser = new User(); + likeUser.firstName = 'Umed'; + likeUser.lastName = 'Khudoiberdiev'; + expect(transformedUser).toEqual(likeUser); + }); + + it('should exclude all prefixed properties if prefix is given', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + $filename: string; + status: number; + } + + class User { + $system: string; + _firstName: string; + _lastName: string; + + @Exclude() + password: string; + + @Type(() => Photo) + photo: Photo; + + @Expose() + get name(): string { + return this._firstName + ' ' + this._lastName; + } + } + + const user = new User(); + user.$system = '@#$%^&*token(*&^%$#@!'; + user._firstName = 'Umed'; + user._lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = new Photo(); + user.photo.id = 1; + user.photo.$filename = 'myphoto.jpg'; + user.photo.status = 1; + + const fromPlainUser = { + $system: '@#$%^&*token(*&^%$#@!', + _firstName: 'Khudoiberdiev', + _lastName: 'imnosuperman', + password: 'imnosuperman', + photo: { + id: 1, + $filename: 'myphoto.jpg', + status: 1, + }, + }; + + const plainUser: any = classToPlain(user, { excludePrefixes: ['_', '$'] }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + name: 'Umed Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + }); + + const transformedUser = plainToClass(User, fromPlainUser, { excludePrefixes: ['_', '$'] }); + expect(transformedUser).toBeInstanceOf(User); + const likeUser = new User(); + likeUser.photo = new Photo(); + likeUser.photo.id = 1; + likeUser.photo.status = 1; + expect(transformedUser).toEqual(likeUser); + }); + + it('should transform array', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + lastName: string; + + @Exclude() + password: string; + + @Expose() + get name(): string { + return this.firstName + ' ' + this.lastName; + } + } + + const user1 = new User(); + user1.firstName = 'Umed'; + user1.lastName = 'Khudoiberdiev'; + user1.password = 'imnosuperman'; + + const user2 = new User(); + user2.firstName = 'Dima'; + user2.lastName = 'Zotov'; + user2.password = 'imnomesser'; + + const users = [user1, user2]; + + const plainUsers: any = classToPlain(users); + expect(plainUsers).toEqual([ + { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + }, + { + firstName: 'Dima', + lastName: 'Zotov', + name: 'Dima Zotov', + }, + ]); + + const fromPlainUsers = [ + { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + }, + { + firstName: 'Dima', + lastName: 'Zotov', + name: 'Dima Zotov', + }, + ]; + + const existUsers = [ + { id: 1, age: 27 }, + { id: 2, age: 30 }, + ]; + const plainUser2 = classToPlainFromExist(users, existUsers); + expect(plainUser2).toEqual([ + { + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + }, + { + id: 2, + age: 30, + firstName: 'Dima', + lastName: 'Zotov', + name: 'Dima Zotov', + }, + ]); + + const transformedUser = plainToClass(User, fromPlainUsers); + + expect(transformedUser[0]).toBeInstanceOf(User); + expect(transformedUser[1]).toBeInstanceOf(User); + const likeUser1 = new User(); + likeUser1.firstName = 'Umed'; + likeUser1.lastName = 'Khudoiberdiev'; + + const likeUser2 = new User(); + likeUser2.firstName = 'Dima'; + likeUser2.lastName = 'Zotov'; + expect(transformedUser).toEqual([likeUser1, likeUser2]); + + const classToClassUsers = classToClass(users); + expect(classToClassUsers[0]).toBeInstanceOf(User); + expect(classToClassUsers[1]).toBeInstanceOf(User); + expect(classToClassUsers[0]).not.toEqual(user1); + expect(classToClassUsers[1]).not.toEqual(user1); + + const classUserLike1 = new User(); + classUserLike1.firstName = 'Umed'; + classUserLike1.lastName = 'Khudoiberdiev'; + + const classUserLike2 = new User(); + classUserLike2.firstName = 'Dima'; + classUserLike2.lastName = 'Zotov'; + + expect(classToClassUsers).toEqual([classUserLike1, classUserLike2]); + + const fromExistUser1 = new User(); + fromExistUser1.id = 1; + + const fromExistUser2 = new User(); + fromExistUser2.id = 2; + + const fromExistUsers = [fromExistUser1, fromExistUser2]; + + const classToClassFromExistUser = classToClassFromExist(users, fromExistUsers); + expect(classToClassFromExistUser[0]).toBeInstanceOf(User); + expect(classToClassFromExistUser[1]).toBeInstanceOf(User); + expect(classToClassFromExistUser[0]).not.toEqual(user1); + expect(classToClassFromExistUser[1]).not.toEqual(user1); + expect(classToClassFromExistUser).toEqual(fromExistUsers); + + const fromExistUserLike1 = new User(); + fromExistUserLike1.id = 1; + fromExistUserLike1.firstName = 'Umed'; + fromExistUserLike1.lastName = 'Khudoiberdiev'; + + const fromExistUserLike2 = new User(); + fromExistUserLike2.id = 2; + fromExistUserLike2.firstName = 'Dima'; + fromExistUserLike2.lastName = 'Zotov'; + + expect(classToClassFromExistUser).toEqual([fromExistUserLike1, fromExistUserLike2]); + }); + + it('should transform objects with null prototype', () => { + class TestClass { + prop: string; + } + + const obj = Object.create(null); + obj.a = 'JS FTW'; + + const transformedClass = plainToClass(TestClass, obj); + expect(transformedClass).toBeInstanceOf(TestClass); + }); + + it('should not pollute the prototype with a `__proto__` property', () => { + const object = JSON.parse('{"__proto__": { "admin": true }}'); + const plainObject = {}; + classToPlainFromExist(object, plainObject); + expect((plainObject as any).admin).toEqual(undefined); + }); + + it('should not pollute the prototype with a `constructor.prototype` property', () => { + const object = JSON.parse('{"constructor": { "prototype": { "admin": true }}}'); + const plainObject = {}; + classToPlainFromExist(object, plainObject); + expect((plainObject as any).admin).toEqual(undefined); + }); + + it('should default union types where the plain type is an array to an array result', () => { + class User { + name: string; + } + + class TestClass { + @Type(() => User) + usersDefined: User[] | undefined; + + @Type(() => User) + usersUndefined: User[] | undefined; + } + + const obj = Object.create(null); + obj.usersDefined = [{ name: 'a-name' }]; + obj.usersUndefined = undefined; + + const transformedClass = plainToClass(TestClass, obj as Record); + + expect(transformedClass).toBeInstanceOf(TestClass); + + expect(transformedClass.usersDefined).toBeInstanceOf(Array); + expect(transformedClass.usersDefined.length).toEqual(1); + expect(transformedClass.usersDefined[0]).toBeInstanceOf(User); + expect(transformedClass.usersDefined[0].name).toEqual('a-name'); + + expect(transformedClass.usersUndefined).toBeUndefined(); + }); }); diff --git a/test/functional/circular-reference-problem.spec.ts b/test/functional/circular-reference-problem.spec.ts index 8d66e065a..d1045edd0 100644 --- a/test/functional/circular-reference-problem.spec.ts +++ b/test/functional/circular-reference-problem.spec.ts @@ -1,148 +1,151 @@ -import "reflect-metadata"; -import {classToClass, classToPlain, plainToClass} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; -import {TransformOperationExecutor} from "../../src/TransformOperationExecutor"; - -describe("circular reference problem", () => { - it("should skip circular reference objects in classToPlain operation", () => { - defaultMetadataStorage.clear(); - - class Caption { - text: string; - } - - class Photo { - id: number; - filename: string; - user: User; - users: User[]; - caption: Caption; - } - - class User { - id: number; - firstName: string; - caption: Caption; - photos: Photo[]; - } - - const photo1 = new Photo(); - photo1.id = 1; - photo1.filename = "me.jpg"; - - const photo2 = new Photo(); - photo2.id = 2; - photo2.filename = "she.jpg"; - - const caption = new Caption(); - caption.text = "cool photo"; - - const user = new User(); - user.caption = caption; - user.firstName = "Umed Khudoiberdiev"; - user.photos = [photo1, photo2]; - - photo1.user = user; - photo2.user = user; - photo1.users = [user]; - photo2.users = [user]; - - photo1.caption = caption; - photo2.caption = caption; - - const plainUser = classToPlain(user, { enableCircularCheck: true }); - expect(plainUser).toEqual({ - firstName: "Umed Khudoiberdiev", - caption: { text: "cool photo" }, - photos: [{ - id: 1, - filename: "me.jpg", - users: [], - caption: { text: "cool photo" } - }, { - id: 2, - filename: "she.jpg", - users: [], - caption: { text: "cool photo" } - }] - }); +import 'reflect-metadata'; +import { classToClass, classToPlain, plainToClass } from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { TransformOperationExecutor } from '../../src/TransformOperationExecutor'; + +describe('circular reference problem', () => { + it('should skip circular reference objects in classToPlain operation', () => { + defaultMetadataStorage.clear(); + + class Caption { + text: string; + } + + class Photo { + id: number; + filename: string; + user: User; + users: User[]; + caption: Caption; + } + + class User { + id: number; + firstName: string; + caption: Caption; + photos: Photo[]; + } + + const photo1 = new Photo(); + photo1.id = 1; + photo1.filename = 'me.jpg'; + + const photo2 = new Photo(); + photo2.id = 2; + photo2.filename = 'she.jpg'; + + const caption = new Caption(); + caption.text = 'cool photo'; + + const user = new User(); + user.caption = caption; + user.firstName = 'Umed Khudoiberdiev'; + user.photos = [photo1, photo2]; + + photo1.user = user; + photo2.user = user; + photo1.users = [user]; + photo2.users = [user]; + + photo1.caption = caption; + photo2.caption = caption; + + const plainUser = classToPlain(user, { enableCircularCheck: true }); + expect(plainUser).toEqual({ + firstName: 'Umed Khudoiberdiev', + caption: { text: 'cool photo' }, + photos: [ + { + id: 1, + filename: 'me.jpg', + users: [], + caption: { text: 'cool photo' }, + }, + { + id: 2, + filename: 'she.jpg', + users: [], + caption: { text: 'cool photo' }, + }, + ], + }); + }); + + it('should not skip circular reference objects, but handle it correctly in classToClass operation', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + filename: string; + user: User; + users: User[]; + } + + class User { + id: number; + firstName: string; + photos: Photo[]; + } + + const photo1 = new Photo(); + photo1.id = 1; + photo1.filename = 'me.jpg'; + + const photo2 = new Photo(); + photo2.id = 2; + photo2.filename = 'she.jpg'; + + const user = new User(); + user.firstName = 'Umed Khudoiberdiev'; + user.photos = [photo1, photo2]; + + photo1.user = user; + photo2.user = user; + photo1.users = [user]; + photo2.users = [user]; + + const classUser = classToClass(user, { enableCircularCheck: true }); + expect(classUser).not.toBe(user); + expect(classUser).toBeInstanceOf(User); + expect(classUser).toEqual(user); + }); + + describe('enableCircularCheck option', () => { + class Photo { + id: number; + filename: string; + } + + class User { + id: number; + firstName: string; + photos: Photo[]; + } + let isCircularSpy: jest.SpyInstance; + const photo1 = new Photo(); + photo1.id = 1; + photo1.filename = 'me.jpg'; + + const user = new User(); + user.firstName = 'Umed Khudoiberdiev'; + user.photos = [photo1]; + + beforeEach(() => { + isCircularSpy = jest.spyOn(TransformOperationExecutor.prototype, 'isCircular' as any); + }); + + afterEach(() => { + isCircularSpy.mockRestore(); }); - it("should not skip circular reference objects, but handle it correctly in classToClass operation", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - filename: string; - user: User; - users: User[]; - } - - class User { - id: number; - firstName: string; - photos: Photo[]; - } - - const photo1 = new Photo(); - photo1.id = 1; - photo1.filename = "me.jpg"; - - const photo2 = new Photo(); - photo2.id = 2; - photo2.filename = "she.jpg"; - - const user = new User(); - user.firstName = "Umed Khudoiberdiev"; - user.photos = [photo1, photo2]; - - photo1.user = user; - photo2.user = user; - photo1.users = [user]; - photo2.users = [user]; - - const classUser = classToClass(user, { enableCircularCheck: true }); - expect(classUser).not.toBe(user); - expect(classUser).toBeInstanceOf(User); - expect(classUser).toEqual(user); + it('enableCircularCheck option is undefined (default)', () => { + plainToClass>(User, user); + expect(isCircularSpy).not.toHaveBeenCalled(); }); - describe("enableCircularCheck option", () => { - class Photo { - id: number; - filename: string; - } - - class User { - id: number; - firstName: string; - photos: Photo[]; - } - let isCircularSpy: jest.SpyInstance; - const photo1 = new Photo(); - photo1.id = 1; - photo1.filename = "me.jpg"; - - const user = new User(); - user.firstName = "Umed Khudoiberdiev"; - user.photos = [photo1]; - - beforeEach(() => { - isCircularSpy = jest.spyOn(TransformOperationExecutor.prototype, "isCircular" as any); - }); - - afterEach(() => { - isCircularSpy.mockRestore(); - }); - - it("enableCircularCheck option is undefined (default)", () => { - plainToClass>(User, user); - expect(isCircularSpy).not.toHaveBeenCalled(); - }); - - it("enableCircularCheck option is true", () => { - plainToClass>(User, user, { enableCircularCheck: true }); - expect(isCircularSpy).toHaveBeenCalled(); - }); + it('enableCircularCheck option is true', () => { + plainToClass>(User, user, { enableCircularCheck: true }); + expect(isCircularSpy).toHaveBeenCalled(); }); + }); }); diff --git a/test/functional/custom-transform.spec.ts b/test/functional/custom-transform.spec.ts index f20ee6f32..036b0afc6 100644 --- a/test/functional/custom-transform.spec.ts +++ b/test/functional/custom-transform.spec.ts @@ -1,709 +1,717 @@ /* eslint-disable @typescript-eslint/camelcase */ -import "reflect-metadata"; -import {classToClass, classToPlain, plainToClass} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; -import {Expose, Transform, Type} from "../../src/decorators"; -import {TransformationType} from "../../src/TransformOperationExecutor"; -import dayjs from "dayjs"; - -describe("custom transformation decorator", () => { - it("@Expose decorator with \"name\" option should work with @Transform decorator", () => { - defaultMetadataStorage.clear(); - - class User { - @Expose({ name: "user_name" }) - @Transform(value => value.toUpperCase()) - name: string; - } - - const plainUser = { - user_name: "Johny Cage" - }; - - const classedUser = plainToClass(User, plainUser); - expect(classedUser.name).toEqual("JOHNY CAGE"); - }); - - it("@Transform decorator logic should be executed depend of toPlainOnly and toClassOnly set", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - name: string; - - @Transform(value => value.toString(), { toPlainOnly: true }) - @Transform(value => dayjs(value), { toClassOnly: true }) - date: Date; - } - - const plainUser = { - id: 1, - name: "Johny Cage", - date: new Date().valueOf() - }; - - const user = new User(); - user.id = 1; - user.name = "Johny Cage"; - user.date = new Date(); - - const classedUser = plainToClass(User, plainUser); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser.id).toEqual(1); - expect(classedUser.name).toEqual("Johny Cage"); - expect(dayjs.isDayjs(classedUser.date)).toBeTruthy(); - - const plainedUser = classToPlain(user); - expect(plainedUser).not.toBeInstanceOf(User); - expect(plainedUser).toEqual({ - id: 1, - name: "Johny Cage", - date: user.date.toString() - }); - }); - - it("versions and groups should work with @Transform decorator too", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - name: string; - - @Type(() => Date) - @Transform(value => dayjs(value), { since: 1, until: 2 }) - date: Date; - - @Type(() => Date) - @Transform(value => value.toString(), { groups: ["user"] }) - lastVisitDate: Date; - } - - const plainUser = { - id: 1, - name: "Johny Cage", - date: new Date().valueOf(), - lastVisitDate: new Date().valueOf() - }; - - const classedUser1 = plainToClass(User, plainUser); - expect(classedUser1).toBeInstanceOf(User); - expect(classedUser1.id).toEqual(1); - expect(classedUser1.name).toEqual("Johny Cage"); - expect(dayjs.isDayjs(classedUser1.date)).toBeTruthy(); - - const classedUser2 = plainToClass(User, plainUser, { version: 0.5 }); - expect(classedUser2).toBeInstanceOf(User); - expect(classedUser2.id).toEqual(1); - expect(classedUser2.name).toEqual("Johny Cage"); - expect(classedUser2.date).toBeInstanceOf(Date); - - const classedUser3 = plainToClass(User, plainUser, { version: 1 }); - expect(classedUser3).toBeInstanceOf(User); - expect(classedUser3.id).toEqual(1); - expect(classedUser3.name).toEqual("Johny Cage"); - expect(dayjs.isDayjs(classedUser3.date)).toBeTruthy(); - - const classedUser4 = plainToClass(User, plainUser, { version: 2 }); - expect(classedUser4).toBeInstanceOf(User); - expect(classedUser4.id).toEqual(1); - expect(classedUser4.name).toEqual("Johny Cage"); - expect(classedUser4.date).toBeInstanceOf(Date); - - const classedUser5 = plainToClass(User, plainUser, { groups: ["user"] }); - expect(classedUser5).toBeInstanceOf(User); - expect(classedUser5.id).toEqual(1); - expect(classedUser5.name).toEqual("Johny Cage"); - expect(classedUser5.lastVisitDate).toEqual(new Date(plainUser.lastVisitDate).toString()); - }); - - it("@Transform decorator callback should be given correct arguments", () => { - defaultMetadataStorage.clear(); - - let objArg: any; - let typeArg: TransformationType; - - function transformCallback(value: any, obj: any, type: TransformationType): any { - objArg = obj; - typeArg = type; - return value; - } - - class User { - @Transform(transformCallback, { toPlainOnly: true }) - @Transform(transformCallback, { toClassOnly: true }) - name: string; - } - - const plainUser = { - name: "Johny Cage", - }; - - plainToClass(User, plainUser); - expect(objArg).toEqual(plainUser); - expect(typeArg).toEqual(TransformationType.PLAIN_TO_CLASS); - - const user = new User(); - user.name = "Johny Cage"; - - classToPlain(user); - expect(objArg).toEqual(user); - expect(typeArg).toEqual(TransformationType.CLASS_TO_PLAIN); - }); - - let model: any; - it("should serialize json into model instance of class Person", () => { - defaultMetadataStorage.clear(); - expect(() => { - const json = { - name: "John Doe", - address: { - street: "Main Street 25", - tel: "5454-534-645", - zip: 10353, - country: "West Samoa" - }, - age: 25, - hobbies: [ - { type: "sport", name: "sailing" }, - { type: "relax", name: "reading" }, - { type: "sport", name: "jogging" }, - { type: "relax", name: "movies" } - ] - }; - class Hobby { - public type: string; - public name: string; - } - class Address { - public street: string; - - @Expose({ name: "tel" }) - public telephone: string; - - public zip: number; - - public country: string; - } - class Person { - public name: string; - - @Type(() => Address) - public address: Address; - - @Type(() => Hobby) - @Transform(value => value.filter((hobby: any) => hobby.type === "sport"), { toClassOnly: true }) - public hobbies: Hobby[]; - - public age: number; - } - model = plainToClass(Person, json); - expect(model instanceof Person); - expect(model.address instanceof Address); - model.hobbies.forEach((hobby: Hobby) => expect(hobby instanceof Hobby && hobby.type === "sport")); - }).not.toThrow(); - }); - - it("should serialize json into model instance of class Person with different possibilities for type of one property (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - const json = { - name: "John Doe", - hobby: { __type: "program", name: "typescript coding", specialAbility: "testing" } - }; - - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [ - { value: Sports, name: "sports" }, { value: Relaxing, name: "relax" }, { value: Programming, name: "program" } - ] - } - }) - public hobby: any; - } - - const expectedHobby = { name: "typescript coding", specialAbility: "TESTING" }; - - const model: Person = plainToClass(Person, json); - expect(model).toBeInstanceOf(Person); - expect(model.hobby).toBeInstanceOf(Programming); - expect(model.hobby).not.toHaveProperty("__type"); - expect(model.hobby).toHaveProperty("specialAbility", "TESTING"); - }).not.toThrow(); - }); - - it("should serialize json into model instance of class Person with different types in array (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - const json = { - name: "John Doe", - hobbies: [ - { __type: "program", name: "typescript coding", specialAbility: "testing" }, - { __type: "relax", name: "sun" } - ] - }; - - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [ - { value: Sports, name: "sports" }, { value: Relaxing, name: "relax" }, { value: Programming, name: "program" } - ] - } - }) - public hobbies: any[]; - } - - - const model: Person = plainToClass(Person, json); - expect(model).toBeInstanceOf(Person); - expect(model.hobbies[0]).toBeInstanceOf(Programming); - expect(model.hobbies[1]).toBeInstanceOf(Relaxing); - expect(model.hobbies[0]).not.toHaveProperty("__type"); - expect(model.hobbies[1]).not.toHaveProperty("__type"); - expect(model.hobbies[1]).toHaveProperty("name", "sun"); - expect(model.hobbies[0]).toHaveProperty("specialAbility", "TESTING"); - }).not.toThrow(); - }); - - it("should serialize json into model instance of class Person with different possibilities for type of one property AND keeps discriminator property (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - const json = { - name: "John Doe", - hobby: { __type: "program", name: "typescript coding", specialAbility: "testing" } - }; - - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [ - { value: Sports, name: "sports" }, { value: Relaxing, name: "relax" }, { value: Programming, name: "program" } - ] - }, - keepDiscriminatorProperty: true - }) - public hobby: any; - } - - const model: Person = plainToClass(Person, json); - expect(model).toBeInstanceOf(Person); - expect(model.hobby).toBeInstanceOf(Programming); - expect(model.hobby).toHaveProperty("__type"); - expect(model.hobby).toHaveProperty("specialAbility", "TESTING"); - }).not.toThrow(); - }); - - it("should serialize json into model instance of class Person with different types in array AND keeps discriminator property (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - const json = { - name: "John Doe", - hobbies: [ - { __type: "program", name: "typescript coding", specialAbility: "testing" }, - { __type: "relax", name: "sun" } - ] - }; - - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [ - { value: Sports, name: "sports" }, { value: Relaxing, name: "relax" }, { value: Programming, name: "program" } - ] - }, - keepDiscriminatorProperty: true - }) - public hobbies: any[]; - } - - const model: Person = plainToClass(Person, json); - expect(model).toBeInstanceOf(Person); - expect(model.hobbies[0]).toBeInstanceOf(Programming); - expect(model.hobbies[1]).toBeInstanceOf(Relaxing); - expect(model.hobbies[0]).toHaveProperty("__type"); - expect(model.hobbies[1]).toHaveProperty("__type"); - expect(model.hobbies[1]).toHaveProperty("name", "sun"); - expect(model.hobbies[0]).toHaveProperty("specialAbility", "TESTING"); - }).not.toThrow(); - }); - - it("should deserialize class Person into json with different possibilities for type of one property (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [ - { value: Sports, name: "sports" }, { value: Relaxing, name: "relax" }, { value: Programming, name: "program" } - ] - } - }) - public hobby: any; - } - - const model: Person = new Person(); - const program = new Programming(); - program.name = "typescript coding"; - program.specialAbility = "testing"; - model.name = "John Doe"; - model.hobby = program; - const json: any = classToPlain(model); - expect(json).not.toBeInstanceOf(Person); - expect(json.hobby).toHaveProperty("__type", "program"); - }).not.toThrow(); - }); - - it("should deserialize class Person into json with different types in array (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [ - { value: Sports, name: "sports" }, { value: Relaxing, name: "relax" }, { value: Programming, name: "program" } - ] - } - }) - public hobbies: any[]; - } - - const model: Person = new Person(); - const sport = new Sports(); - sport.name = "Football"; - const program = new Programming(); - program.name = "typescript coding"; - program.specialAbility = "testing"; - model.name = "John Doe"; - model.hobbies = [ - sport, - program - ]; - const json: any = classToPlain(model); - expect(json).not.toBeInstanceOf(Person); - expect(json.hobbies[0]).toHaveProperty("__type", "sports"); - expect(json.hobbies[1]).toHaveProperty("__type", "program"); - }).not.toThrow(); - }); - - it("should transform class Person into class OtherPerson with different possibilities for type of one property (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [ - { value: Sports, name: "sports" }, { value: Relaxing, name: "relax" }, { value: Programming, name: "program" } - ] - } - }) - public hobby: any; - } - - const model: Person = new Person(); - const program = new Programming(); - program.name = "typescript coding"; - program.specialAbility = "testing"; - model.name = "John Doe"; - model.hobby = program; - const person: Person = classToClass(model); - expect(person).toBeInstanceOf(Person); - expect(person.hobby).not.toHaveProperty("__type"); - }).not.toThrow(); - }); - - it("should transform class Person into class OtherPerson with different types in array (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [ - { value: Sports, name: "sports" }, { value: Relaxing, name: "relax" }, { value: Programming, name: "program" } - ] - } - }) - public hobbies: any[]; - } - - const model: Person = new Person(); - const sport = new Sports(); - sport.name = "Football"; - const program = new Programming(); - program.name = "typescript coding"; - program.specialAbility = "testing"; - model.name = "John Doe"; - model.hobbies = [ - sport, - program - ]; - const person: Person = classToClass(model); - expect(person).toBeInstanceOf(Person); - expect(person.hobbies[0]).not.toHaveProperty("__type"); - expect(person.hobbies[1]).not.toHaveProperty("__type"); - }).not.toThrow(); - }); - - it("should serialize json into model instance of class Person with different possibilities for type of one property AND uses default as fallback (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - const json = { - name: "John Doe", - hobby: { __type: "program", name: "typescript coding", specialAbility: "testing" } - }; - - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [] - }, - }) - public hobby: any; - } - - const model: Person = plainToClass(Person, json); - expect(model).toBeInstanceOf(Person); - expect(model.hobby).toBeInstanceOf(Hobby); - expect(model.hobby).not.toHaveProperty("__type"); - expect(model.hobby).toHaveProperty("specialAbility", "testing"); - }).not.toThrow(); - }); - - it("should serialize json into model instance of class Person with different types in array AND uses default as fallback (polymorphism)", () => { - defaultMetadataStorage.clear(); - expect(() => { - const json = { - name: "John Doe", - hobbies: [ - { __type: "program", name: "typescript coding", specialAbility: "testing" }, - { __type: "relax", name: "sun" } - ] - }; - - abstract class Hobby { - public name: string; - } - - class Sports extends Hobby { - // Empty - } - - class Relaxing extends Hobby { - // Empty - } - - class Programming extends Hobby { - @Transform((value: string) => value.toUpperCase()) - specialAbility: string; - } - - class Person { - public name: string; - - @Type(() => Hobby, { - discriminator: { - property: "__type", - subTypes: [] - }, - }) - public hobbies: any[]; - } - - - const model: Person = plainToClass(Person, json); - expect(model).toBeInstanceOf(Person); - expect(model.hobbies[0]).toBeInstanceOf(Hobby); - expect(model.hobbies[1]).toBeInstanceOf(Hobby); - expect(model.hobbies[0]).not.toHaveProperty("__type"); - expect(model.hobbies[1]).not.toHaveProperty("__type"); - expect(model.hobbies[1]).toHaveProperty("name", "sun"); - expect(model.hobbies[0]).toHaveProperty("specialAbility", "testing"); - }).not.toThrow(); - }); - - it("should serialize a model into json", () => { - expect(() => { - classToPlain(model); - }).not.toThrow(); +import 'reflect-metadata'; +import { classToClass, classToPlain, plainToClass } from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { Expose, Transform, Type } from '../../src/decorators'; +import { TransformationType } from '../../src/TransformOperationExecutor'; +import dayjs from 'dayjs'; + +describe('custom transformation decorator', () => { + it('@Expose decorator with "name" option should work with @Transform decorator', () => { + defaultMetadataStorage.clear(); + + class User { + @Expose({ name: 'user_name' }) + @Transform(value => value.toUpperCase()) + name: string; + } + + const plainUser = { + user_name: 'Johny Cage', + }; + + const classedUser = plainToClass(User, plainUser); + expect(classedUser.name).toEqual('JOHNY CAGE'); + }); + + it('@Transform decorator logic should be executed depend of toPlainOnly and toClassOnly set', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + name: string; + + @Transform(value => value.toString(), { toPlainOnly: true }) + @Transform(value => dayjs(value), { toClassOnly: true }) + date: Date; + } + + const plainUser = { + id: 1, + name: 'Johny Cage', + date: new Date().valueOf(), + }; + + const user = new User(); + user.id = 1; + user.name = 'Johny Cage'; + user.date = new Date(); + + const classedUser = plainToClass(User, plainUser); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser.id).toEqual(1); + expect(classedUser.name).toEqual('Johny Cage'); + expect(dayjs.isDayjs(classedUser.date)).toBeTruthy(); + + const plainedUser = classToPlain(user); + expect(plainedUser).not.toBeInstanceOf(User); + expect(plainedUser).toEqual({ + id: 1, + name: 'Johny Cage', + date: user.date.toString(), }); + }); + + it('versions and groups should work with @Transform decorator too', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + name: string; + + @Type(() => Date) + @Transform(value => dayjs(value), { since: 1, until: 2 }) + date: Date; + + @Type(() => Date) + @Transform(value => value.toString(), { groups: ['user'] }) + lastVisitDate: Date; + } + + const plainUser = { + id: 1, + name: 'Johny Cage', + date: new Date().valueOf(), + lastVisitDate: new Date().valueOf(), + }; + + const classedUser1 = plainToClass(User, plainUser); + expect(classedUser1).toBeInstanceOf(User); + expect(classedUser1.id).toEqual(1); + expect(classedUser1.name).toEqual('Johny Cage'); + expect(dayjs.isDayjs(classedUser1.date)).toBeTruthy(); + + const classedUser2 = plainToClass(User, plainUser, { version: 0.5 }); + expect(classedUser2).toBeInstanceOf(User); + expect(classedUser2.id).toEqual(1); + expect(classedUser2.name).toEqual('Johny Cage'); + expect(classedUser2.date).toBeInstanceOf(Date); + + const classedUser3 = plainToClass(User, plainUser, { version: 1 }); + expect(classedUser3).toBeInstanceOf(User); + expect(classedUser3.id).toEqual(1); + expect(classedUser3.name).toEqual('Johny Cage'); + expect(dayjs.isDayjs(classedUser3.date)).toBeTruthy(); + + const classedUser4 = plainToClass(User, plainUser, { version: 2 }); + expect(classedUser4).toBeInstanceOf(User); + expect(classedUser4.id).toEqual(1); + expect(classedUser4.name).toEqual('Johny Cage'); + expect(classedUser4.date).toBeInstanceOf(Date); + + const classedUser5 = plainToClass(User, plainUser, { groups: ['user'] }); + expect(classedUser5).toBeInstanceOf(User); + expect(classedUser5.id).toEqual(1); + expect(classedUser5.name).toEqual('Johny Cage'); + expect(classedUser5.lastVisitDate).toEqual(new Date(plainUser.lastVisitDate).toString()); + }); + + it('@Transform decorator callback should be given correct arguments', () => { + defaultMetadataStorage.clear(); + + let objArg: any; + let typeArg: TransformationType; + + function transformCallback(value: any, obj: any, type: TransformationType): any { + objArg = obj; + typeArg = type; + return value; + } + + class User { + @Transform(transformCallback, { toPlainOnly: true }) + @Transform(transformCallback, { toClassOnly: true }) + name: string; + } + + const plainUser = { + name: 'Johny Cage', + }; + + plainToClass(User, plainUser); + expect(objArg).toEqual(plainUser); + expect(typeArg).toEqual(TransformationType.PLAIN_TO_CLASS); + + const user = new User(); + user.name = 'Johny Cage'; + + classToPlain(user); + expect(objArg).toEqual(user); + expect(typeArg).toEqual(TransformationType.CLASS_TO_PLAIN); + }); + + let model: any; + it('should serialize json into model instance of class Person', () => { + defaultMetadataStorage.clear(); + expect(() => { + const json = { + name: 'John Doe', + address: { + street: 'Main Street 25', + tel: '5454-534-645', + zip: 10353, + country: 'West Samoa', + }, + age: 25, + hobbies: [ + { type: 'sport', name: 'sailing' }, + { type: 'relax', name: 'reading' }, + { type: 'sport', name: 'jogging' }, + { type: 'relax', name: 'movies' }, + ], + }; + class Hobby { + public type: string; + public name: string; + } + class Address { + public street: string; + + @Expose({ name: 'tel' }) + public telephone: string; + + public zip: number; + + public country: string; + } + class Person { + public name: string; + + @Type(() => Address) + public address: Address; + + @Type(() => Hobby) + @Transform(value => value.filter((hobby: any) => hobby.type === 'sport'), { toClassOnly: true }) + public hobbies: Hobby[]; + + public age: number; + } + model = plainToClass(Person, json); + expect(model instanceof Person); + expect(model.address instanceof Address); + model.hobbies.forEach((hobby: Hobby) => expect(hobby instanceof Hobby && hobby.type === 'sport')); + }).not.toThrow(); + }); + + it('should serialize json into model instance of class Person with different possibilities for type of one property (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + const json = { + name: 'John Doe', + hobby: { __type: 'program', name: 'typescript coding', specialAbility: 'testing' }, + }; + + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [ + { value: Sports, name: 'sports' }, + { value: Relaxing, name: 'relax' }, + { value: Programming, name: 'program' }, + ], + }, + }) + public hobby: any; + } + + const expectedHobby = { name: 'typescript coding', specialAbility: 'TESTING' }; + + const model: Person = plainToClass(Person, json); + expect(model).toBeInstanceOf(Person); + expect(model.hobby).toBeInstanceOf(Programming); + expect(model.hobby).not.toHaveProperty('__type'); + expect(model.hobby).toHaveProperty('specialAbility', 'TESTING'); + }).not.toThrow(); + }); + + it('should serialize json into model instance of class Person with different types in array (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + const json = { + name: 'John Doe', + hobbies: [ + { __type: 'program', name: 'typescript coding', specialAbility: 'testing' }, + { __type: 'relax', name: 'sun' }, + ], + }; + + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [ + { value: Sports, name: 'sports' }, + { value: Relaxing, name: 'relax' }, + { value: Programming, name: 'program' }, + ], + }, + }) + public hobbies: any[]; + } + + const model: Person = plainToClass(Person, json); + expect(model).toBeInstanceOf(Person); + expect(model.hobbies[0]).toBeInstanceOf(Programming); + expect(model.hobbies[1]).toBeInstanceOf(Relaxing); + expect(model.hobbies[0]).not.toHaveProperty('__type'); + expect(model.hobbies[1]).not.toHaveProperty('__type'); + expect(model.hobbies[1]).toHaveProperty('name', 'sun'); + expect(model.hobbies[0]).toHaveProperty('specialAbility', 'TESTING'); + }).not.toThrow(); + }); + + it('should serialize json into model instance of class Person with different possibilities for type of one property AND keeps discriminator property (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + const json = { + name: 'John Doe', + hobby: { __type: 'program', name: 'typescript coding', specialAbility: 'testing' }, + }; + + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [ + { value: Sports, name: 'sports' }, + { value: Relaxing, name: 'relax' }, + { value: Programming, name: 'program' }, + ], + }, + keepDiscriminatorProperty: true, + }) + public hobby: any; + } + + const model: Person = plainToClass(Person, json); + expect(model).toBeInstanceOf(Person); + expect(model.hobby).toBeInstanceOf(Programming); + expect(model.hobby).toHaveProperty('__type'); + expect(model.hobby).toHaveProperty('specialAbility', 'TESTING'); + }).not.toThrow(); + }); + + it('should serialize json into model instance of class Person with different types in array AND keeps discriminator property (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + const json = { + name: 'John Doe', + hobbies: [ + { __type: 'program', name: 'typescript coding', specialAbility: 'testing' }, + { __type: 'relax', name: 'sun' }, + ], + }; + + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [ + { value: Sports, name: 'sports' }, + { value: Relaxing, name: 'relax' }, + { value: Programming, name: 'program' }, + ], + }, + keepDiscriminatorProperty: true, + }) + public hobbies: any[]; + } + + const model: Person = plainToClass(Person, json); + expect(model).toBeInstanceOf(Person); + expect(model.hobbies[0]).toBeInstanceOf(Programming); + expect(model.hobbies[1]).toBeInstanceOf(Relaxing); + expect(model.hobbies[0]).toHaveProperty('__type'); + expect(model.hobbies[1]).toHaveProperty('__type'); + expect(model.hobbies[1]).toHaveProperty('name', 'sun'); + expect(model.hobbies[0]).toHaveProperty('specialAbility', 'TESTING'); + }).not.toThrow(); + }); + + it('should deserialize class Person into json with different possibilities for type of one property (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [ + { value: Sports, name: 'sports' }, + { value: Relaxing, name: 'relax' }, + { value: Programming, name: 'program' }, + ], + }, + }) + public hobby: any; + } + + const model: Person = new Person(); + const program = new Programming(); + program.name = 'typescript coding'; + program.specialAbility = 'testing'; + model.name = 'John Doe'; + model.hobby = program; + const json: any = classToPlain(model); + expect(json).not.toBeInstanceOf(Person); + expect(json.hobby).toHaveProperty('__type', 'program'); + }).not.toThrow(); + }); + + it('should deserialize class Person into json with different types in array (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [ + { value: Sports, name: 'sports' }, + { value: Relaxing, name: 'relax' }, + { value: Programming, name: 'program' }, + ], + }, + }) + public hobbies: any[]; + } + + const model: Person = new Person(); + const sport = new Sports(); + sport.name = 'Football'; + const program = new Programming(); + program.name = 'typescript coding'; + program.specialAbility = 'testing'; + model.name = 'John Doe'; + model.hobbies = [sport, program]; + const json: any = classToPlain(model); + expect(json).not.toBeInstanceOf(Person); + expect(json.hobbies[0]).toHaveProperty('__type', 'sports'); + expect(json.hobbies[1]).toHaveProperty('__type', 'program'); + }).not.toThrow(); + }); + + it('should transform class Person into class OtherPerson with different possibilities for type of one property (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [ + { value: Sports, name: 'sports' }, + { value: Relaxing, name: 'relax' }, + { value: Programming, name: 'program' }, + ], + }, + }) + public hobby: any; + } + + const model: Person = new Person(); + const program = new Programming(); + program.name = 'typescript coding'; + program.specialAbility = 'testing'; + model.name = 'John Doe'; + model.hobby = program; + const person: Person = classToClass(model); + expect(person).toBeInstanceOf(Person); + expect(person.hobby).not.toHaveProperty('__type'); + }).not.toThrow(); + }); + + it('should transform class Person into class OtherPerson with different types in array (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [ + { value: Sports, name: 'sports' }, + { value: Relaxing, name: 'relax' }, + { value: Programming, name: 'program' }, + ], + }, + }) + public hobbies: any[]; + } + + const model: Person = new Person(); + const sport = new Sports(); + sport.name = 'Football'; + const program = new Programming(); + program.name = 'typescript coding'; + program.specialAbility = 'testing'; + model.name = 'John Doe'; + model.hobbies = [sport, program]; + const person: Person = classToClass(model); + expect(person).toBeInstanceOf(Person); + expect(person.hobbies[0]).not.toHaveProperty('__type'); + expect(person.hobbies[1]).not.toHaveProperty('__type'); + }).not.toThrow(); + }); + + it('should serialize json into model instance of class Person with different possibilities for type of one property AND uses default as fallback (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + const json = { + name: 'John Doe', + hobby: { __type: 'program', name: 'typescript coding', specialAbility: 'testing' }, + }; + + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [], + }, + }) + public hobby: any; + } + + const model: Person = plainToClass(Person, json); + expect(model).toBeInstanceOf(Person); + expect(model.hobby).toBeInstanceOf(Hobby); + expect(model.hobby).not.toHaveProperty('__type'); + expect(model.hobby).toHaveProperty('specialAbility', 'testing'); + }).not.toThrow(); + }); + + it('should serialize json into model instance of class Person with different types in array AND uses default as fallback (polymorphism)', () => { + defaultMetadataStorage.clear(); + expect(() => { + const json = { + name: 'John Doe', + hobbies: [ + { __type: 'program', name: 'typescript coding', specialAbility: 'testing' }, + { __type: 'relax', name: 'sun' }, + ], + }; + + abstract class Hobby { + public name: string; + } + + class Sports extends Hobby { + // Empty + } + + class Relaxing extends Hobby { + // Empty + } + + class Programming extends Hobby { + @Transform((value: string) => value.toUpperCase()) + specialAbility: string; + } + + class Person { + public name: string; + + @Type(() => Hobby, { + discriminator: { + property: '__type', + subTypes: [], + }, + }) + public hobbies: any[]; + } + + const model: Person = plainToClass(Person, json); + expect(model).toBeInstanceOf(Person); + expect(model.hobbies[0]).toBeInstanceOf(Hobby); + expect(model.hobbies[1]).toBeInstanceOf(Hobby); + expect(model.hobbies[0]).not.toHaveProperty('__type'); + expect(model.hobbies[1]).not.toHaveProperty('__type'); + expect(model.hobbies[1]).toHaveProperty('name', 'sun'); + expect(model.hobbies[0]).toHaveProperty('specialAbility', 'testing'); + }).not.toThrow(); + }); + + it('should serialize a model into json', () => { + expect(() => { + classToPlain(model); + }).not.toThrow(); + }); }); diff --git a/test/functional/es6-data-types.spec.ts b/test/functional/es6-data-types.spec.ts index 96c867a7b..b8ef4507d 100644 --- a/test/functional/es6-data-types.spec.ts +++ b/test/functional/es6-data-types.spec.ts @@ -1,270 +1,255 @@ -import "reflect-metadata"; -import {classToPlain, plainToClass} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; -import {Type} from "../../src/decorators"; - -describe("es6 data types", () => { - it("using Map", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - name: string; - @Type(() => String) - weapons: Map; - } - - const plainUser = { - id: 1, - name: "Max Pain", - weapons: { - firstWeapon: "knife", - secondWeapon: "eagle", - thirdWeapon: "ak-47", - } - }; - - const weapons = new Map(); - weapons.set("firstWeapon", "knife"); - weapons.set("secondWeapon", "eagle"); - weapons.set("thirdWeapon", "ak-47"); - - const user = new User(); - user.id = 1; - user.name = "Max Pain"; - user.weapons = weapons; - - const classedUser = plainToClass(User, plainUser); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser.id).toEqual(1); - expect(classedUser.name).toEqual("Max Pain"); - expect(classedUser.weapons).toBeInstanceOf(Map); - expect(classedUser.weapons.size).toEqual(3); - expect(classedUser.weapons.get("firstWeapon")).toEqual("knife"); - expect(classedUser.weapons.get("secondWeapon")).toEqual("eagle"); - expect(classedUser.weapons.get("thirdWeapon")).toEqual("ak-47"); - - const plainedUser = classToPlain(user); - expect(plainedUser).not.toBeInstanceOf(User); - expect(plainedUser).toEqual({ - id: 1, - name: "Max Pain", - weapons: { - firstWeapon: "knife", - secondWeapon: "eagle", - thirdWeapon: "ak-47", - } - }); - +import 'reflect-metadata'; +import { classToPlain, plainToClass } from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { Type } from '../../src/decorators'; + +describe('es6 data types', () => { + it('using Map', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + name: string; + @Type(() => String) + weapons: Map; + } + + const plainUser = { + id: 1, + name: 'Max Pain', + weapons: { + firstWeapon: 'knife', + secondWeapon: 'eagle', + thirdWeapon: 'ak-47', + }, + }; + + const weapons = new Map(); + weapons.set('firstWeapon', 'knife'); + weapons.set('secondWeapon', 'eagle'); + weapons.set('thirdWeapon', 'ak-47'); + + const user = new User(); + user.id = 1; + user.name = 'Max Pain'; + user.weapons = weapons; + + const classedUser = plainToClass(User, plainUser); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser.id).toEqual(1); + expect(classedUser.name).toEqual('Max Pain'); + expect(classedUser.weapons).toBeInstanceOf(Map); + expect(classedUser.weapons.size).toEqual(3); + expect(classedUser.weapons.get('firstWeapon')).toEqual('knife'); + expect(classedUser.weapons.get('secondWeapon')).toEqual('eagle'); + expect(classedUser.weapons.get('thirdWeapon')).toEqual('ak-47'); + + const plainedUser = classToPlain(user); + expect(plainedUser).not.toBeInstanceOf(User); + expect(plainedUser).toEqual({ + id: 1, + name: 'Max Pain', + weapons: { + firstWeapon: 'knife', + secondWeapon: 'eagle', + thirdWeapon: 'ak-47', + }, }); - - it("using Set", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - name: string; - @Type(() => Set) - weapons: Set; - } - - const plainUser = { - id: 1, - name: "Max Pain", - weapons: [ - "knife", - "eagle", - "ak-47" - ] - }; - - const weapons = new Set(); - weapons.add("knife"); - weapons.add("eagle"); - weapons.add("ak-47"); - - const user = new User(); - user.id = 1; - user.name = "Max Pain"; - user.weapons = weapons; - - const classedUser = plainToClass(User, plainUser); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser.id).toEqual(1); - expect(classedUser.name).toEqual("Max Pain"); - expect(classedUser.weapons).toBeInstanceOf(Set); - expect(classedUser.weapons.size).toEqual(3); - expect(classedUser.weapons.has("knife")).toBeTruthy(); - expect(classedUser.weapons.has("eagle")).toBeTruthy(); - expect(classedUser.weapons.has("ak-47")).toBeTruthy(); - - const plainedUser = classToPlain(user); - expect(plainedUser).not.toBeInstanceOf(User); - expect(plainedUser).toEqual({ - id: 1, - name: "Max Pain", - weapons: [ - "knife", - "eagle", - "ak-47" - ] - }); - + }); + + it('using Set', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + name: string; + @Type(() => Set) + weapons: Set; + } + + const plainUser = { + id: 1, + name: 'Max Pain', + weapons: ['knife', 'eagle', 'ak-47'], + }; + + const weapons = new Set(); + weapons.add('knife'); + weapons.add('eagle'); + weapons.add('ak-47'); + + const user = new User(); + user.id = 1; + user.name = 'Max Pain'; + user.weapons = weapons; + + const classedUser = plainToClass(User, plainUser); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser.id).toEqual(1); + expect(classedUser.name).toEqual('Max Pain'); + expect(classedUser.weapons).toBeInstanceOf(Set); + expect(classedUser.weapons.size).toEqual(3); + expect(classedUser.weapons.has('knife')).toBeTruthy(); + expect(classedUser.weapons.has('eagle')).toBeTruthy(); + expect(classedUser.weapons.has('ak-47')).toBeTruthy(); + + const plainedUser = classToPlain(user); + expect(plainedUser).not.toBeInstanceOf(User); + expect(plainedUser).toEqual({ + id: 1, + name: 'Max Pain', + weapons: ['knife', 'eagle', 'ak-47'], }); - - it("using Map with objects", () => { - defaultMetadataStorage.clear(); - - class Weapon { - constructor(public model: string, - public range: number) { - } - } - - class User { - id: number; - name: string; - @Type(() => Weapon) - weapons: Map; - } - - const plainUser = { - id: 1, - name: "Max Pain", - weapons: { - firstWeapon: { - model: "knife", - range: 1 - }, - secondWeapon: { - model: "eagle", - range: 200 - }, - thirdWeapon: { - model: "ak-47", - range: 800 - } - } - }; - - const weapons = new Map(); - weapons.set("firstWeapon", new Weapon("knife", 1)); - weapons.set("secondWeapon", new Weapon("eagle", 200)); - weapons.set("thirdWeapon", new Weapon("ak-47", 800)); - - const user = new User(); - user.id = 1; - user.name = "Max Pain"; - user.weapons = weapons; - - const classedUser = plainToClass(User, plainUser); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser.id).toEqual(1); - expect(classedUser.name).toEqual("Max Pain"); - expect(classedUser.weapons).toBeInstanceOf(Map); - expect(classedUser.weapons.size).toEqual(3); - expect(classedUser.weapons.get("firstWeapon")).toBeInstanceOf(Weapon); - expect(classedUser.weapons.get("firstWeapon")).toEqual({ - model: "knife", - range: 1 - }); - expect(classedUser.weapons.get("secondWeapon")).toBeInstanceOf(Weapon); - expect(classedUser.weapons.get("secondWeapon")).toEqual({ - model: "eagle", - range: 200 - }); - expect(classedUser.weapons.get("thirdWeapon")).toBeInstanceOf(Weapon); - expect(classedUser.weapons.get("thirdWeapon")).toEqual({ - model: "ak-47", - range: 800 - }); - - const plainedUser = classToPlain(user); - expect(plainedUser).not.toBeInstanceOf(User); - expect(plainedUser).toEqual({ - id: 1, - name: "Max Pain", - weapons: { - firstWeapon: { - model: "knife", - range: 1 - }, - secondWeapon: { - model: "eagle", - range: 200 - }, - thirdWeapon: { - model: "ak-47", - range: 800 - } - } - }); - + }); + + it('using Map with objects', () => { + defaultMetadataStorage.clear(); + + class Weapon { + constructor(public model: string, public range: number) {} + } + + class User { + id: number; + name: string; + @Type(() => Weapon) + weapons: Map; + } + + const plainUser = { + id: 1, + name: 'Max Pain', + weapons: { + firstWeapon: { + model: 'knife', + range: 1, + }, + secondWeapon: { + model: 'eagle', + range: 200, + }, + thirdWeapon: { + model: 'ak-47', + range: 800, + }, + }, + }; + + const weapons = new Map(); + weapons.set('firstWeapon', new Weapon('knife', 1)); + weapons.set('secondWeapon', new Weapon('eagle', 200)); + weapons.set('thirdWeapon', new Weapon('ak-47', 800)); + + const user = new User(); + user.id = 1; + user.name = 'Max Pain'; + user.weapons = weapons; + + const classedUser = plainToClass(User, plainUser); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser.id).toEqual(1); + expect(classedUser.name).toEqual('Max Pain'); + expect(classedUser.weapons).toBeInstanceOf(Map); + expect(classedUser.weapons.size).toEqual(3); + expect(classedUser.weapons.get('firstWeapon')).toBeInstanceOf(Weapon); + expect(classedUser.weapons.get('firstWeapon')).toEqual({ + model: 'knife', + range: 1, + }); + expect(classedUser.weapons.get('secondWeapon')).toBeInstanceOf(Weapon); + expect(classedUser.weapons.get('secondWeapon')).toEqual({ + model: 'eagle', + range: 200, + }); + expect(classedUser.weapons.get('thirdWeapon')).toBeInstanceOf(Weapon); + expect(classedUser.weapons.get('thirdWeapon')).toEqual({ + model: 'ak-47', + range: 800, }); - it("using Set with objects", () => { - defaultMetadataStorage.clear(); - - class Weapon { - constructor(public model: string, - public range: number) { - } - } - - class User { - id: number; - name: string; - @Type(() => Weapon) - weapons: Set; - } - - const plainUser = { - id: 1, - name: "Max Pain", - weapons: [ - { model: "knife", range: 1 }, - { model: "eagle", range: 200 }, - { model: "ak-47", range: 800 }, - ] - }; - - const weapons = new Set(); - weapons.add(new Weapon("knife", 1)); - weapons.add(new Weapon("eagle", 200)); - weapons.add(new Weapon("ak-47", 800)); - - const user = new User(); - user.id = 1; - user.name = "Max Pain"; - user.weapons = weapons; - - const classedUser = plainToClass(User, plainUser); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser.id).toEqual(1); - expect(classedUser.name).toEqual("Max Pain"); - expect(classedUser.weapons).toBeInstanceOf(Set); - expect(classedUser.weapons.size).toEqual(3); - const it = classedUser.weapons.values(); - const first = it.next().value; - const second = it.next().value; - const third = it.next().value; - expect(first).toBeInstanceOf(Weapon); - expect(first).toEqual({ model: "knife", range: 1 }); - expect(second).toBeInstanceOf(Weapon); - expect(second).toEqual({ model: "eagle", range: 200 }); - expect(third).toBeInstanceOf(Weapon); - expect(third).toEqual({ model: "ak-47", range: 800 }); - - const plainedUser = classToPlain(user); - expect(plainedUser).not.toBeInstanceOf(User); - expect(plainedUser).toEqual({ - id: 1, - name: "Max Pain", - weapons: [ - { model: "knife", range: 1 }, - { model: "eagle", range: 200 }, - { model: "ak-47", range: 800 }, - ] - }); + const plainedUser = classToPlain(user); + expect(plainedUser).not.toBeInstanceOf(User); + expect(plainedUser).toEqual({ + id: 1, + name: 'Max Pain', + weapons: { + firstWeapon: { + model: 'knife', + range: 1, + }, + secondWeapon: { + model: 'eagle', + range: 200, + }, + thirdWeapon: { + model: 'ak-47', + range: 800, + }, + }, + }); + }); + + it('using Set with objects', () => { + defaultMetadataStorage.clear(); + + class Weapon { + constructor(public model: string, public range: number) {} + } + + class User { + id: number; + name: string; + @Type(() => Weapon) + weapons: Set; + } + + const plainUser = { + id: 1, + name: 'Max Pain', + weapons: [ + { model: 'knife', range: 1 }, + { model: 'eagle', range: 200 }, + { model: 'ak-47', range: 800 }, + ], + }; + + const weapons = new Set(); + weapons.add(new Weapon('knife', 1)); + weapons.add(new Weapon('eagle', 200)); + weapons.add(new Weapon('ak-47', 800)); + + const user = new User(); + user.id = 1; + user.name = 'Max Pain'; + user.weapons = weapons; + + const classedUser = plainToClass(User, plainUser); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser.id).toEqual(1); + expect(classedUser.name).toEqual('Max Pain'); + expect(classedUser.weapons).toBeInstanceOf(Set); + expect(classedUser.weapons.size).toEqual(3); + const it = classedUser.weapons.values(); + const first = it.next().value; + const second = it.next().value; + const third = it.next().value; + expect(first).toBeInstanceOf(Weapon); + expect(first).toEqual({ model: 'knife', range: 1 }); + expect(second).toBeInstanceOf(Weapon); + expect(second).toEqual({ model: 'eagle', range: 200 }); + expect(third).toBeInstanceOf(Weapon); + expect(third).toEqual({ model: 'ak-47', range: 800 }); + + const plainedUser = classToPlain(user); + expect(plainedUser).not.toBeInstanceOf(User); + expect(plainedUser).toEqual({ + id: 1, + name: 'Max Pain', + weapons: [ + { model: 'knife', range: 1 }, + { model: 'eagle', range: 200 }, + { model: 'ak-47', range: 800 }, + ], }); + }); }); diff --git a/test/functional/ignore-decorators.spec.ts b/test/functional/ignore-decorators.spec.ts index ffbcb742c..6ae4a5b04 100644 --- a/test/functional/ignore-decorators.spec.ts +++ b/test/functional/ignore-decorators.spec.ts @@ -1,35 +1,35 @@ -import "reflect-metadata"; -import {classToPlain} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; -import {Exclude, Expose} from "../../src/decorators"; +import 'reflect-metadata'; +import { classToPlain } from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { Exclude, Expose } from '../../src/decorators'; -describe("ignoring specific decorators", () => { - it("when ignoreDecorators is set to true it should ignore all decorators", () => { - defaultMetadataStorage.clear(); +describe('ignoring specific decorators', () => { + it('when ignoreDecorators is set to true it should ignore all decorators', () => { + defaultMetadataStorage.clear(); - class User { - id: number; + class User { + id: number; - @Expose({ name: "lala" }) - firstName: string; + @Expose({ name: 'lala' }) + firstName: string; - @Expose({ groups: ["user"] }) - lastName: string; + @Expose({ groups: ['user'] }) + lastName: string; - @Exclude() - password: string; - } + @Exclude() + password: string; + } - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; - const plainedUser = classToPlain(user, { ignoreDecorators: true }); - expect(plainedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); + const plainedUser = classToPlain(user, { ignoreDecorators: true }); + expect(plainedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', }); + }); }); diff --git a/test/functional/implicit-type-declarations.spec.ts b/test/functional/implicit-type-declarations.spec.ts index e0ff39bab..a762c2ee1 100644 --- a/test/functional/implicit-type-declarations.spec.ts +++ b/test/functional/implicit-type-declarations.spec.ts @@ -1,128 +1,144 @@ -import "reflect-metadata"; -import {plainToClass,} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; -import {Expose, Type} from "../../src/decorators"; - -describe("implicit type conversion", () => { - it("should run only when enabled", () => { - defaultMetadataStorage.clear(); - - class SimpleExample { - @Expose() - readonly implicitTypeNumber: number; - - @Expose() - readonly implicitTypeString: string; - } - - const result1: SimpleExample = plainToClass(SimpleExample, { - implicitTypeNumber: "100", - implicitTypeString: 133123, - }, {enableImplicitConversion: true}); - - const result2: SimpleExample = plainToClass(SimpleExample, { - implicitTypeNumber: "100", - implicitTypeString: 133123, - }, {enableImplicitConversion: false}); - - expect(result1).toEqual({implicitTypeNumber: 100, implicitTypeString: "133123"}); - expect(result2).toEqual({implicitTypeNumber: "100", implicitTypeString: 133123}); - }); -}); +import 'reflect-metadata'; +import { plainToClass } from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { Expose, Type } from '../../src/decorators'; -describe("implicit and explicity type declarations", () => { +describe('implicit type conversion', () => { + it('should run only when enabled', () => { defaultMetadataStorage.clear(); - class Example { - @Expose() - readonly implicitTypeViaOtherDecorator: Date; - - @Type() - readonly implicitTypeViaEmptyTypeDecorator: number; + class SimpleExample { + @Expose() + readonly implicitTypeNumber: number; - @Type(() => String) - readonly explicitType: string; + @Expose() + readonly implicitTypeString: string; } - const result: Example = plainToClass(Example, { - implicitTypeViaOtherDecorator: "2018-12-24T12:00:00Z", - implicitTypeViaEmptyTypeDecorator: "100", - explicitType: 100, - }, {enableImplicitConversion: true}); - - it("should use implicitly defined design:type to convert value when no @Type decorator is used", () => { - expect(result.implicitTypeViaOtherDecorator).toBeInstanceOf(Date); - expect(result.implicitTypeViaOtherDecorator.getTime()).toEqual(new Date("2018-12-24T12:00:00Z").getTime()); - }); - - it("should use implicitly defined design:type to convert value when empty @Type() decorator is used", () => { - expect(typeof result.implicitTypeViaEmptyTypeDecorator).toBe("number"); - expect(result.implicitTypeViaEmptyTypeDecorator).toEqual(100); - }); - - it("should use explicitly defined type when @Type(() => Construtable) decorator is used", () => { - expect(typeof result.explicitType).toBe("string"); - expect(result.explicitType).toEqual("100"); - }); + const result1: SimpleExample = plainToClass( + SimpleExample, + { + implicitTypeNumber: '100', + implicitTypeString: 133123, + }, + { enableImplicitConversion: true } + ); + + const result2: SimpleExample = plainToClass( + SimpleExample, + { + implicitTypeNumber: '100', + implicitTypeString: 133123, + }, + { enableImplicitConversion: false } + ); + + expect(result1).toEqual({ implicitTypeNumber: 100, implicitTypeString: '133123' }); + expect(result2).toEqual({ implicitTypeNumber: '100', implicitTypeString: 133123 }); + }); }); -describe("plainToClass transforms built-in primitive types properly", () => { - defaultMetadataStorage.clear(); - - class Example { - @Type() - date: Date; - - @Type() - string: string; - - @Type() - string2: string; - - @Type() - number: number; - - @Type() - number2: number; - - @Type() - boolean: boolean; - - @Type() - boolean2: boolean; - } +describe('implicit and explicity type declarations', () => { + defaultMetadataStorage.clear(); + + class Example { + @Expose() + readonly implicitTypeViaOtherDecorator: Date; + + @Type() + readonly implicitTypeViaEmptyTypeDecorator: number; + + @Type(() => String) + readonly explicitType: string; + } + + const result: Example = plainToClass( + Example, + { + implicitTypeViaOtherDecorator: '2018-12-24T12:00:00Z', + implicitTypeViaEmptyTypeDecorator: '100', + explicitType: 100, + }, + { enableImplicitConversion: true } + ); + + it('should use implicitly defined design:type to convert value when no @Type decorator is used', () => { + expect(result.implicitTypeViaOtherDecorator).toBeInstanceOf(Date); + expect(result.implicitTypeViaOtherDecorator.getTime()).toEqual(new Date('2018-12-24T12:00:00Z').getTime()); + }); + + it('should use implicitly defined design:type to convert value when empty @Type() decorator is used', () => { + expect(typeof result.implicitTypeViaEmptyTypeDecorator).toBe('number'); + expect(result.implicitTypeViaEmptyTypeDecorator).toEqual(100); + }); + + it('should use explicitly defined type when @Type(() => Construtable) decorator is used', () => { + expect(typeof result.explicitType).toBe('string'); + expect(result.explicitType).toEqual('100'); + }); +}); - const result: Example = plainToClass(Example, { - date: "2018-12-24T12:00:00Z", - string: "100", - string2: 100, - number: "100", - number2: 100, - boolean: 1, - boolean2: 0, - }, {enableImplicitConversion: true}); - - it("should recognize and convert to Date", () => { - expect(result.date).toBeInstanceOf(Date); - expect(result.date.getTime()).toEqual(new Date("2018-12-24T12:00:00Z").getTime()); - }); - - it("should recognize and convert to string", () => { - expect(typeof result.string).toBe("string"); - expect(typeof result.string2).toBe("string"); - expect(result.string).toEqual("100"); - expect(result.string2).toEqual("100"); - }); - - it("should recognize and convert to number", () => { - expect(typeof result.number).toBe("number"); - expect(typeof result.number2).toBe("number"); - expect(result.number).toEqual(100); - expect(result.number2).toEqual(100); - }); - - it("should recognize and convert to boolean", () => { - expect(result.boolean).toBeTruthy(); - expect(result.boolean2).toBeFalsy(); - }); +describe('plainToClass transforms built-in primitive types properly', () => { + defaultMetadataStorage.clear(); + + class Example { + @Type() + date: Date; + + @Type() + string: string; + + @Type() + string2: string; + + @Type() + number: number; + + @Type() + number2: number; + + @Type() + boolean: boolean; + + @Type() + boolean2: boolean; + } + + const result: Example = plainToClass( + Example, + { + date: '2018-12-24T12:00:00Z', + string: '100', + string2: 100, + number: '100', + number2: 100, + boolean: 1, + boolean2: 0, + }, + { enableImplicitConversion: true } + ); + + it('should recognize and convert to Date', () => { + expect(result.date).toBeInstanceOf(Date); + expect(result.date.getTime()).toEqual(new Date('2018-12-24T12:00:00Z').getTime()); + }); + + it('should recognize and convert to string', () => { + expect(typeof result.string).toBe('string'); + expect(typeof result.string2).toBe('string'); + expect(result.string).toEqual('100'); + expect(result.string2).toEqual('100'); + }); + + it('should recognize and convert to number', () => { + expect(typeof result.number).toBe('number'); + expect(typeof result.number2).toBe('number'); + expect(result.number).toEqual(100); + expect(result.number2).toEqual(100); + }); + + it('should recognize and convert to boolean', () => { + expect(result.boolean).toBeTruthy(); + expect(result.boolean2).toBeFalsy(); + }); }); diff --git a/test/functional/inheritence.spec.ts b/test/functional/inheritence.spec.ts index df3c4558c..4b17e3db1 100644 --- a/test/functional/inheritence.spec.ts +++ b/test/functional/inheritence.spec.ts @@ -1,42 +1,42 @@ -import "reflect-metadata"; -import {plainToClass, Transform, Type} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; +import 'reflect-metadata'; +import { plainToClass, Transform, Type } from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; -describe("inheritence", () => { - it("decorators should work inside a base class", () => { - defaultMetadataStorage.clear(); +describe('inheritence', () => { + it('decorators should work inside a base class', () => { + defaultMetadataStorage.clear(); - class Contact { - @Transform(value => value.toUpperCase()) - name: string; - @Type(() => Date) - birthDate: Date; - } + class Contact { + @Transform(value => value.toUpperCase()) + name: string; + @Type(() => Date) + birthDate: Date; + } - class User extends Contact { - @Type(() => Number) - id: number; - email: string; - } + class User extends Contact { + @Type(() => Number) + id: number; + email: string; + } - class Student extends User { - @Transform(value => value.toUpperCase()) - university: string; - } + class Student extends User { + @Transform(value => value.toUpperCase()) + university: string; + } - const plainStudent = { - name: "Johny Cage", - university: "mit", - birthDate: new Date(1967, 2, 1).toDateString(), - id: 100, - email: "johnny.cage@gmail.com" - }; + const plainStudent = { + name: 'Johny Cage', + university: 'mit', + birthDate: new Date(1967, 2, 1).toDateString(), + id: 100, + email: 'johnny.cage@gmail.com', + }; - const classedStudent = plainToClass(Student, plainStudent); - expect(classedStudent.name).toEqual("JOHNY CAGE"); - expect(classedStudent.university).toEqual("MIT"); - expect(classedStudent.birthDate.getTime()).toEqual(new Date(1967, 2, 1).getTime()); - expect(classedStudent.id).toEqual(plainStudent.id); - expect(classedStudent.email).toEqual(plainStudent.email); - }); + const classedStudent = plainToClass(Student, plainStudent); + expect(classedStudent.name).toEqual('JOHNY CAGE'); + expect(classedStudent.university).toEqual('MIT'); + expect(classedStudent.birthDate.getTime()).toEqual(new Date(1967, 2, 1).getTime()); + expect(classedStudent.id).toEqual(plainStudent.id); + expect(classedStudent.email).toEqual(plainStudent.email); + }); }); diff --git a/test/functional/serialization-deserialization.spec.ts b/test/functional/serialization-deserialization.spec.ts index 7b3710100..201f226c9 100644 --- a/test/functional/serialization-deserialization.spec.ts +++ b/test/functional/serialization-deserialization.spec.ts @@ -1,126 +1,135 @@ -import "reflect-metadata"; -import {deserialize, deserializeArray, serialize} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; -import {Exclude} from "../../src/decorators"; - -describe("serialization and deserialization objects", () => { - it("should perform serialization and deserialization properly", () => { - defaultMetadataStorage.clear(); - - class User { - firstName: string; - lastName: string; - @Exclude() - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const user1 = new User(); - user1.firstName = "Dima"; - user1.lastName = "Zotov"; - user1.password = "imnosuperman"; - - const user2 = new User(); - user2.firstName = "Bakhrom"; - user2.lastName = "Baubekov"; - user2.password = "imnosuperman"; - - const users = [user1, user2]; - const plainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const plainUsers = [{ - firstName: "Dima", - lastName: "Zotov", - password: "imnobatman" - }, { - firstName: "Bakhrom", - lastName: "Baubekov", - password: "imnosuperman" - }]; - - const plainedUser = serialize(user); - expect(plainedUser).toEqual(JSON.stringify({ - firstName: "Umed", - lastName: "Khudoiberdiev" - })); - - const plainedUsers = serialize(users); - expect(plainedUsers).toEqual(JSON.stringify([{ - firstName: "Dima", - lastName: "Zotov", - }, { - firstName: "Bakhrom", - lastName: "Baubekov", - }])); - - const classedUser = deserialize(User, JSON.stringify(plainUser)); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classedUsers = deserializeArray(User, JSON.stringify(plainUsers)); - expect(classedUsers[0]).toBeInstanceOf(User); - expect(classedUsers[1]).toBeInstanceOf(User); - - const userLike1 = new User(); - userLike1.firstName = "Dima"; - userLike1.lastName = "Zotov"; - - const userLike2 = new User(); - userLike2.firstName = "Bakhrom"; - userLike2.lastName = "Baubekov"; - - expect(classedUsers).toEqual([userLike1, userLike2]); +import 'reflect-metadata'; +import { deserialize, deserializeArray, serialize } from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { Exclude } from '../../src/decorators'; + +describe('serialization and deserialization objects', () => { + it('should perform serialization and deserialization properly', () => { + defaultMetadataStorage.clear(); + + class User { + firstName: string; + lastName: string; + @Exclude() + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const user1 = new User(); + user1.firstName = 'Dima'; + user1.lastName = 'Zotov'; + user1.password = 'imnosuperman'; + + const user2 = new User(); + user2.firstName = 'Bakhrom'; + user2.lastName = 'Baubekov'; + user2.password = 'imnosuperman'; + + const users = [user1, user2]; + const plainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const plainUsers = [ + { + firstName: 'Dima', + lastName: 'Zotov', + password: 'imnobatman', + }, + { + firstName: 'Bakhrom', + lastName: 'Baubekov', + password: 'imnosuperman', + }, + ]; + + const plainedUser = serialize(user); + expect(plainedUser).toEqual( + JSON.stringify({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }) + ); + + const plainedUsers = serialize(users); + expect(plainedUsers).toEqual( + JSON.stringify([ + { + firstName: 'Dima', + lastName: 'Zotov', + }, + { + firstName: 'Bakhrom', + lastName: 'Baubekov', + }, + ]) + ); + + const classedUser = deserialize(User, JSON.stringify(plainUser)); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', }); - it("should successfully deserialize object with unknown nested properties ", () => { - defaultMetadataStorage.clear(); - - class TestObject { - prop: string; - } - - const payload = { - prop: "Hi", - extra: { - anotherProp: "let's see how this works out!" - } - }; - - const result = deserialize(TestObject, JSON.stringify(payload)); - expect(result).toBeInstanceOf(TestObject); - expect(result.prop).toEqual("Hi"); - // TODO: We should strip, but it's a breaking change - // (result).extra.should.be.undefined; - }); - - - it("should not overwrite non writable properties on deserialize", () => { - class TestObject { - get getterOnlyProp(): string { - return "I cannot write!"; - } - - normalProp: string = "Hello!"; - } - - const payload = { - getterOnlyProp: "I CAN write!", - normalProp: "Goodbye!" - }; - - const result = deserialize(TestObject, JSON.stringify(payload)); - expect(result.getterOnlyProp).toEqual("I cannot write!"); - expect(result.normalProp).toEqual("Goodbye!"); - }); + const classedUsers = deserializeArray(User, JSON.stringify(plainUsers)); + expect(classedUsers[0]).toBeInstanceOf(User); + expect(classedUsers[1]).toBeInstanceOf(User); + + const userLike1 = new User(); + userLike1.firstName = 'Dima'; + userLike1.lastName = 'Zotov'; + + const userLike2 = new User(); + userLike2.firstName = 'Bakhrom'; + userLike2.lastName = 'Baubekov'; + + expect(classedUsers).toEqual([userLike1, userLike2]); + }); + + it('should successfully deserialize object with unknown nested properties ', () => { + defaultMetadataStorage.clear(); + + class TestObject { + prop: string; + } + + const payload = { + prop: 'Hi', + extra: { + anotherProp: "let's see how this works out!", + }, + }; + + const result = deserialize(TestObject, JSON.stringify(payload)); + expect(result).toBeInstanceOf(TestObject); + expect(result.prop).toEqual('Hi'); + // TODO: We should strip, but it's a breaking change + // (result).extra.should.be.undefined; + }); + + it('should not overwrite non writable properties on deserialize', () => { + class TestObject { + get getterOnlyProp(): string { + return 'I cannot write!'; + } + + normalProp: string = 'Hello!'; + } + + const payload = { + getterOnlyProp: 'I CAN write!', + normalProp: 'Goodbye!', + }; + + const result = deserialize(TestObject, JSON.stringify(payload)); + expect(result.getterOnlyProp).toEqual('I cannot write!'); + expect(result.normalProp).toEqual('Goodbye!'); + }); }); diff --git a/test/functional/specify-maps.spec.ts b/test/functional/specify-maps.spec.ts index 2eaf2dd4b..398d5dca4 100644 --- a/test/functional/specify-maps.spec.ts +++ b/test/functional/specify-maps.spec.ts @@ -1,1636 +1,1675 @@ -import "reflect-metadata"; -import {classToClass, classToClassFromExist, classToPlain, classToPlainFromExist, plainToClass, plainToClassFromExist} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; -import {Exclude, Expose, Type} from "../../src/decorators"; - -describe("specifying target maps", () => { - it("should convert instance of the given object to plain javascript object and should expose all properties since its a default behaviour", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - lastName: string; - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - - const existUser = { id: 1, age: 27 }; - const plainUser2 = classToPlainFromExist(user, existUser); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toBe(user); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - }); - - it("should exclude all objects marked with @Exclude() decorator", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - lastName: string; - @Exclude() - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser.password).toBeUndefined(); - - const existUser = { id: 1, age: 27, password: "yayayaya" }; - const plainUser2 = classToPlainFromExist(user, existUser); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "yayayaya" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - }); - - it("should exclude all properties from object if whole class is marked with @Exclude() decorator", () => { - defaultMetadataStorage.clear(); - - @Exclude() - class User { - id: number; - firstName: string; - lastName: string; - password: string; - } - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({}); - expect(plainUser.firstName).toBeUndefined(); - expect(plainUser.lastName).toBeUndefined(); - expect(plainUser.password).toBeUndefined(); - - const existUser = { id: 1, age: 27 }; - const plainUser2 = classToPlainFromExist(user, existUser); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27 - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({}); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1 - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({}); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1 - }); - }); - - it("should exclude all properties from object if whole class is marked with @Exclude() decorator, but include properties marked with @Expose() decorator", () => { - defaultMetadataStorage.clear(); - - @Exclude() - class User { - id: number; - - @Expose() - firstName: string; - - @Expose() - lastName: string; - - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser.password).toBeUndefined(); - - const existUser = { id: 1, age: 27 }; - const plainUser2 = classToPlainFromExist(user, existUser); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - }); - - it("should exclude all properties from object if its defined via transformation options, but include properties marked with @Expose() decorator", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - - @Expose() - firstName: string; - - @Expose() - lastName: string; - - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user, { strategy: "excludeAll" }); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser.password).toBeUndefined(); - - const existUser = { id: 1, age: 27 }; - const plainUser2 = classToPlainFromExist(user, existUser, { strategy: "excludeAll" }); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser, { strategy: "excludeAll" }); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: "excludeAll" }); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassUser = classToClass(user, { strategy: "excludeAll" }); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: "excludeAll" }); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - }); - - it("should expose all properties from object if its defined via transformation options, but exclude properties marked with @Exclude() decorator", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - - @Exclude() - lastName: string; - - @Exclude() - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user, { strategy: "exposeAll" }); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed" - }); - expect(plainUser.lastName).toBeUndefined(); - expect(plainUser.password).toBeUndefined(); - - const existUser = { id: 1, age: 27 }; - const plainUser2 = classToPlainFromExist(user, existUser, { strategy: "exposeAll" }); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed" - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser, { strategy: "exposeAll" }); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "Umed" - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: "exposeAll" }); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed" - }); - - const classToClassUser = classToClass(user, { strategy: "exposeAll" }); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "Umed" - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: "exposeAll" }); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed" - }); - }); - - it("should convert values to specific types if they are set via @Type decorator", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - - @Type(type => String) - firstName: string; - - @Type(type => String) - lastName: string; - - @Type(type => Number) - password: number; - - @Type(type => Boolean) - isActive: boolean; - - @Type(type => Date) - registrationDate: Date; - - @Type(type => String) - lastVisitDate: string; - } - - const date = new Date(); - const user = new User(); - user.firstName = 321 as any; - user.lastName = 123 as any; - user.password = "123" as any; - user.isActive = "1" as any; - user.registrationDate = date.toString() as any; - user.lastVisitDate = date as any; - - const fromPlainUser = { - firstName: 321, - lastName: 123, - password: "123", - isActive: "1", - registrationDate: date.toString(), - lastVisitDate: date - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - - const plainUser: any = classToPlain(user, { strategy: "exposeAll" }); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - }); - - const existUser = { id: 1, age: 27 }; - const plainUser2 = classToPlainFromExist(user, existUser, { strategy: "exposeAll" }); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - }); - expect(plainUser2).toEqual(existUser); - - const transformedUser = plainToClass(User, fromPlainUser, { strategy: "exposeAll" }); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser).toEqual({ - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: "exposeAll" }); - expect(fromExistTransformedUser).toBeInstanceOf(User); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - }); - - const classToClassUser = classToClass(user, { strategy: "exposeAll" }); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).toEqual({ - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: "exposeAll" }); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "321", - lastName: "123", - password: 123, - isActive: true, - registrationDate: new Date(date.toString()), - lastVisitDate: date.toString(), - }); - }); - - it("should transform nested objects too and make sure their decorators are used too", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - name: string; - - @Exclude() - filename: string; - - uploadDate: Date; - } - - class User { - firstName: string; - lastName: string; - - @Exclude() - password: string; - - photo: Photo; // type should be automatically guessed - } - - const photo = new Photo(); - photo.id = 1; - photo.name = "Me"; - photo.filename = "iam.jpg"; - photo.uploadDate = new Date(); - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = photo; - - const plainUser: any = classToPlain(user, { strategy: "exposeAll" }); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser.photo).not.toBeInstanceOf(Photo); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - uploadDate: photo.uploadDate - } - }); - expect(plainUser.password).toBeUndefined(); - expect(plainUser.photo.filename).toBeUndefined(); - expect(plainUser.photo.uploadDate).toEqual(photo.uploadDate); - expect(plainUser.photo.uploadDate).not.toBe(photo.uploadDate); - - const existUser = { id: 1, age: 27, photo: { id: 2, description: "photo" } }; - const plainUser2: any = classToPlainFromExist(user, existUser, { strategy: "exposeAll" }); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2.photo).not.toBeInstanceOf(Photo); - expect(plainUser2).toEqual({ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - uploadDate: photo.uploadDate, - description: "photo" - } - }); - expect(plainUser2).toEqual(existUser); - expect(plainUser2.password).toBeUndefined(); - expect(plainUser2.photo.filename).toBeUndefined(); - expect(plainUser2.photo.uploadDate).toEqual(photo.uploadDate); - expect(plainUser2.photo.uploadDate).not.toBe(photo.uploadDate); - }); - - it("should transform nested objects too and make sure given type is used instead of automatically guessed one", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - name: string; - - @Exclude() - filename: string; - } - - class ExtendedPhoto implements Photo { - id: number; - - @Exclude() - name: string; - - filename: string; - } - - class User { - id: number; - firstName: string; - lastName: string; - - @Exclude() - password: string; - - @Type(type => ExtendedPhoto) // force specific type - photo: Photo; - } - - const photo = new Photo(); - photo.id = 1; - photo.name = "Me"; - photo.filename = "iam.jpg"; - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = photo; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "iam.jpg" - } - }); - expect(plainUser.password).toBeUndefined(); - expect(plainUser.photo.name).toBeUndefined(); - }); - - it("should convert given plain object to class instance object", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - name: string; - - @Exclude() - filename: string; - - metadata: string; - uploadDate: Date; - } - - class User { - id: number; - firstName: string; - lastName: string; - - @Exclude() - password: string; - - @Type(type => Photo) - photo: Photo; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = new Photo(); - user.photo.id = 1; - user.photo.name = "Me"; - user.photo.filename = "iam.jpg"; - user.photo.uploadDate = new Date(); - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - name: "Me", - filename: "iam.jpg", - uploadDate: new Date(), - } - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - const fromExistPhoto = new Photo(); - fromExistPhoto.metadata = "taken by Camera"; - fromExistUser.photo = fromExistPhoto; - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - expect(transformedUser.photo).toBeInstanceOf(Photo); - expect(transformedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - uploadDate: fromPlainUser.photo.uploadDate - } - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); - expect(fromExistTransformedUser).toEqual(fromExistUser); - expect(fromExistTransformedUser.photo).toEqual(fromExistPhoto); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - metadata: "taken by Camera", - uploadDate: fromPlainUser.photo.uploadDate - } - }); - - const classToClassUser = classToClass(user); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser.photo).toBeInstanceOf(Photo); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).not.toEqual(user.photo); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - uploadDate: user.photo.uploadDate - } - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).not.toEqual(user.photo); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - name: "Me", - metadata: "taken by Camera", - uploadDate: user.photo.uploadDate - } - }); - }); - - it("should expose only properties that match given group", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - - @Expose({ - groups: ["user", "guest"] - }) - filename: string; - - @Expose({ - groups: ["admin"] - }) - status: number; - - metadata: string; - } - - class User { - id: number; - firstName: string; - - @Expose({ - groups: ["user", "guest"] - }) - lastName: string; - - @Expose({ - groups: ["user"] - }) - password: string; - - @Expose({ - groups: ["admin"] - }) - isActive: boolean; - - @Type(type => Photo) - photo: Photo; - - @Expose({ - groups: ["admin"] - }) - @Type(type => Photo) - photos: Photo[]; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.isActive = false; - user.photo = new Photo(); - user.photo.id = 1; - user.photo.filename = "myphoto.jpg"; - user.photo.status = 1; - user.photos = [user.photo]; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - isActive: false, - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1, - }] - }; - - const fromExistUser = new User(); - fromExistUser.id = 1; - fromExistUser.photo = new Photo(); - fromExistUser.photo.metadata = "taken by Camera"; - - const plainUser1: any = classToPlain(user); - expect(plainUser1).not.toBeInstanceOf(User); - expect(plainUser1).toEqual({ - firstName: "Umed", - photo: { - id: 1 - } - }); - expect(plainUser1.lastName).toBeUndefined(); - expect(plainUser1.password).toBeUndefined(); - expect(plainUser1.isActive).toBeUndefined(); - - const plainUser2: any = classToPlain(user, { groups: ["user"] }); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - expect(plainUser2.isActive).toBeUndefined(); - - const transformedUser2 = plainToClass(User, fromPlainUser, { groups: ["user"] }); - expect(transformedUser2).toBeInstanceOf(User); - expect(transformedUser2.photo).toBeInstanceOf(Photo); - expect(transformedUser2).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { groups: ["user"] }); - expect(fromExistTransformedUser).toEqual(fromExistUser); - expect(fromExistTransformedUser.photo).toEqual(fromExistUser.photo); - expect(fromExistTransformedUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - metadata: "taken by Camera", - filename: "myphoto.jpg" - } - }); - - const classToClassUser = classToClass(user, { groups: ["user"] }); - expect(classToClassUser).toBeInstanceOf(User); - expect(classToClassUser.photo).toBeInstanceOf(Photo); - expect(classToClassUser).not.toEqual(user); - expect(classToClassUser).not.toEqual(user.photo); - expect(classToClassUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { groups: ["user"] }); - expect(classToClassFromExistUser).toBeInstanceOf(User); - expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo); - expect(classToClassFromExistUser).not.toEqual(user); - expect(classToClassFromExistUser).not.toEqual(user.photo); - expect(classToClassFromExistUser).toEqual(fromExistUser); - expect(classToClassFromExistUser).toEqual({ - id: 1, - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - metadata: "taken by Camera", - filename: "myphoto.jpg" - } - }); - - const plainUser3: any = classToPlain(user, { groups: ["guest"] }); - expect(plainUser3).not.toBeInstanceOf(User); - expect(plainUser3).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - expect(plainUser3.password).toBeUndefined(); - expect(plainUser3.isActive).toBeUndefined(); - - const transformedUser3 = plainToClass(User, fromPlainUser, { groups: ["guest"] }); - expect(transformedUser3).toBeInstanceOf(User); - expect(transformedUser3.photo).toBeInstanceOf(Photo); - expect(transformedUser3).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const plainUser4: any = classToPlain(user, { groups: ["admin"] }); - expect(plainUser4).not.toBeInstanceOf(User); - expect(plainUser4).toEqual({ - firstName: "Umed", - isActive: false, - photo: { - id: 1, - status: 1 - }, - photos: [{ - id: 1, - status: 1 - }] - }); - expect(plainUser4.lastName).toBeUndefined(); - expect(plainUser4.password).toBeUndefined(); - - const transformedUser4 = plainToClass(User, fromPlainUser, { groups: ["admin"] }); - expect(transformedUser4).toBeInstanceOf(User); - expect(transformedUser4.photo).toBeInstanceOf(Photo); - expect(transformedUser4.photos[0]).toBeInstanceOf(Photo); - expect(transformedUser4).toEqual({ - firstName: "Umed", - isActive: false, - photo: { - id: 1, - status: 1 - }, - photos: [{ - id: 1, - status: 1 - }] - }); - - const plainUser5: any = classToPlain(user, { groups: ["admin", "user"] }); - expect(plainUser5).not.toBeInstanceOf(User); - expect(plainUser5).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - isActive: false, - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1 - }] - }); - - const transformedUser5 = plainToClass(User, fromPlainUser, { groups: ["admin", "user"] }); - expect(transformedUser5).toBeInstanceOf(User); - expect(transformedUser5).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - isActive: false, - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1 - }] - }); - }); - - it("should expose only properties that match given version", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - - @Expose({ - since: 1.5, - until: 2 - }) - filename: string; - - @Expose({ - since: 2 - }) - status: number; - } - - class User { - @Expose({ - since: 1, - until: 2 - }) - firstName: string; - - @Expose({ - since: 0.5 - }) - lastName: string; - - @Exclude() - password: string; - - @Type(type => Photo) - photo: Photo; - - @Expose({ - since: 3 - }) - @Type(type => Photo) - photos: Photo[]; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = new Photo(); - user.photo.id = 1; - user.photo.filename = "myphoto.jpg"; - user.photo.status = 1; - user.photos = [user.photo]; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman", - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1, - }] - }; - - const plainUser1: any = classToPlain(user); - expect(plainUser1).not.toBeInstanceOf(User); - expect(plainUser1).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1 - }] - }); - - const transformedUser1 = plainToClass(User, fromPlainUser); - expect(transformedUser1).toBeInstanceOf(User); - expect(transformedUser1.photo).toBeInstanceOf(Photo); - expect(transformedUser1.photos[0]).toBeInstanceOf(Photo); - expect(transformedUser1).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg", - status: 1 - }, - photos: [{ - id: 1, - filename: "myphoto.jpg", - status: 1 - }] - }); - - const plainUser2: any = classToPlain(user, { version: 0.3 }); - expect(plainUser2).not.toBeInstanceOf(User); - expect(plainUser2).toEqual({ - photo: { - id: 1 - } - }); - - const transformedUser2 = plainToClass(User, fromPlainUser, { version: 0.3 }); - expect(transformedUser2).toBeInstanceOf(User); - expect(transformedUser2.photo).toBeInstanceOf(Photo); - expect(transformedUser2).toEqual({ - photo: { - id: 1 - } - }); - - const plainUser3: any = classToPlain(user, { version: 0.5 }); - expect(plainUser3).not.toBeInstanceOf(User); - expect(plainUser3).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1 - } - }); - - const transformedUser3 = plainToClass(User, fromPlainUser, { version: 0.5 }); - expect(transformedUser3).toBeInstanceOf(User); - expect(transformedUser3.photo).toBeInstanceOf(Photo); - expect(transformedUser3).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1 - } - }); - - const plainUser4: any = classToPlain(user, { version: 1 }); - expect(plainUser4).not.toBeInstanceOf(User); - expect(plainUser4).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1 - } - }); - - const transformedUser4 = plainToClass(User, fromPlainUser, { version: 1 }); - expect(transformedUser4).toBeInstanceOf(User); - expect(transformedUser4.photo).toBeInstanceOf(Photo); - expect(transformedUser4).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1 - } - }); - - const plainUser5: any = classToPlain(user, { version: 1.5 }); - expect(plainUser5).not.toBeInstanceOf(User); - expect(plainUser5).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const transformedUser5 = plainToClass(User, fromPlainUser, { version: 1.5 }); - expect(transformedUser5).toBeInstanceOf(User); - expect(transformedUser5.photo).toBeInstanceOf(Photo); - expect(transformedUser5).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - photo: { - id: 1, - filename: "myphoto.jpg" - } - }); - - const plainUser6: any = classToPlain(user, { version: 2 }); - expect(plainUser6).not.toBeInstanceOf(User); - expect(plainUser6).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1, - status: 1 - } - }); - - const transformedUser6 = plainToClass(User, fromPlainUser, { version: 2 }); - expect(transformedUser6).toBeInstanceOf(User); - expect(transformedUser6.photo).toBeInstanceOf(Photo); - expect(transformedUser6).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1, - status: 1 - } - }); - - const plainUser7: any = classToPlain(user, { version: 3 }); - expect(plainUser7).not.toBeInstanceOf(User); - expect(plainUser7).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1, - status: 1 - }, - photos: [{ - id: 1, - status: 1 - }] - }); - - const transformedUser7 = plainToClass(User, fromPlainUser, { version: 3 }); - expect(transformedUser7).toBeInstanceOf(User); - expect(transformedUser7.photo).toBeInstanceOf(Photo); - expect(transformedUser7.photos[0]).toBeInstanceOf(Photo); - expect(transformedUser7).toEqual({ - lastName: "Khudoiberdiev", - photo: { - id: 1, - status: 1 - }, - photos: [{ - id: 1, - status: 1 - }] - }); - }); - - it("should expose method and accessors that have @Expose()", () => { - defaultMetadataStorage.clear(); - - class User { - firstName: string; - lastName: string; - - @Exclude() - password: string; - - @Expose() - get name(): string { - return this.firstName + " " + this.lastName; - } - - @Expose() - getName(): string { - return this.firstName + " " + this.lastName; - } - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - name: "Umed Khudoiberdiev", - getName: "Umed Khudoiberdiev" - }); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - const likeUser = new User(); - likeUser.firstName = "Umed"; - likeUser.lastName = "Khudoiberdiev"; - expect(transformedUser).toEqual(likeUser); - }); - - it("should expose with alternative name if its given", () => { - defaultMetadataStorage.clear(); - - class User { - @Expose({ name: "myName" }) - firstName: string; - - @Expose({ name: "secondName" }) - lastName: string; - - @Exclude() - password: string; - - @Expose() - get name(): string { - return this.firstName + " " + this.lastName; - } - - @Expose({ name: "fullName" }) - getName(): string { - return this.firstName + " " + this.lastName; - } - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const fromPlainUser = { - myName: "Umed", - secondName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const plainUser: any = classToPlain(user); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - myName: "Umed", - secondName: "Khudoiberdiev", - name: "Umed Khudoiberdiev", - fullName: "Umed Khudoiberdiev" - }); - - const transformedUser = plainToClass(User, fromPlainUser); - expect(transformedUser).toBeInstanceOf(User); - const likeUser = new User(); - likeUser.firstName = "Umed"; - likeUser.lastName = "Khudoiberdiev"; - expect(transformedUser).toEqual(likeUser); - }); - - it("should exclude all prefixed properties if prefix is given", () => { - defaultMetadataStorage.clear(); - - class Photo { - id: number; - $filename: string; - status: number; - } - - class User { - $system: string; - _firstName: string; - _lastName: string; - - @Exclude() - password: string; - - @Type(() => Photo) - photo: Photo; - - @Expose() - get name(): string { - return this._firstName + " " + this._lastName; - } - } - - const user = new User(); - user.$system = "@#$%^&*token(*&^%$#@!"; - user._firstName = "Umed"; - user._lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - user.photo = new Photo(); - user.photo.id = 1; - user.photo.$filename = "myphoto.jpg"; - user.photo.status = 1; - - const fromPlainUser = { - $system: "@#$%^&*token(*&^%$#@!", - _firstName: "Khudoiberdiev", - _lastName: "imnosuperman", - password: "imnosuperman", - photo: { - id: 1, - $filename: "myphoto.jpg", - status: 1, - } - }; - - const plainUser: any = classToPlain(user, { excludePrefixes: ["_", "$"] }); - expect(plainUser).not.toBeInstanceOf(User); - expect(plainUser).toEqual({ - name: "Umed Khudoiberdiev", - photo: { - id: 1, - status: 1 - } - }); - - const transformedUser = plainToClass(User, fromPlainUser, { excludePrefixes: ["_", "$"] }); - expect(transformedUser).toBeInstanceOf(User); - const likeUser = new User(); - likeUser.photo = new Photo(); - likeUser.photo.id = 1; - likeUser.photo.status = 1; - expect(transformedUser).toEqual(likeUser); - }); - - it("should be able to transform array too", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - lastName: string; - - @Exclude() - password: string; - - @Expose() - get name(): string { - return this.firstName + " " + this.lastName; - } - } - - const user1 = new User(); - user1.firstName = "Umed"; - user1.lastName = "Khudoiberdiev"; - user1.password = "imnosuperman"; - - const user2 = new User(); - user2.firstName = "Dima"; - user2.lastName = "Zotov"; - user2.password = "imnomesser"; - - const users = [user1, user2]; - - const plainUsers: any = classToPlain(users); - expect(plainUsers).toEqual([{ - firstName: "Umed", - lastName: "Khudoiberdiev", - name: "Umed Khudoiberdiev" - }, { - firstName: "Dima", - lastName: "Zotov", - name: "Dima Zotov" - }]); - - const fromPlainUsers = [{ - firstName: "Umed", - lastName: "Khudoiberdiev", - name: "Umed Khudoiberdiev" - }, { - firstName: "Dima", - lastName: "Zotov", - name: "Dima Zotov" - }]; - - const existUsers = [{ id: 1, age: 27 }, { id: 2, age: 30 }]; - const plainUser2 = classToPlainFromExist(users, existUsers); - expect(plainUser2).toEqual([{ - id: 1, - age: 27, - firstName: "Umed", - lastName: "Khudoiberdiev", - name: "Umed Khudoiberdiev" - }, { - id: 2, - age: 30, - firstName: "Dima", - lastName: "Zotov", - name: "Dima Zotov" - }]); - - const transformedUser = plainToClass(User, fromPlainUsers); - - expect(transformedUser[0]).toBeInstanceOf(User); - expect(transformedUser[1]).toBeInstanceOf(User); - const likeUser1 = new User(); - likeUser1.firstName = "Umed"; - likeUser1.lastName = "Khudoiberdiev"; - - const likeUser2 = new User(); - likeUser2.firstName = "Dima"; - likeUser2.lastName = "Zotov"; - expect(transformedUser).toEqual([likeUser1, likeUser2]); - - const classToClassUsers = classToClass(users); - expect(classToClassUsers[0]).toBeInstanceOf(User); - expect(classToClassUsers[1]).toBeInstanceOf(User); - expect(classToClassUsers[0]).not.toEqual(user1); - expect(classToClassUsers[1]).not.toEqual(user1); - - const classUserLike1 = new User(); - classUserLike1.firstName = "Umed"; - classUserLike1.lastName = "Khudoiberdiev"; - - const classUserLike2 = new User(); - classUserLike2.firstName = "Dima"; - classUserLike2.lastName = "Zotov"; - - expect(classToClassUsers).toEqual([classUserLike1, classUserLike2]); - - const fromExistUser1 = new User(); - fromExistUser1.id = 1; - - const fromExistUser2 = new User(); - fromExistUser2.id = 2; - - const fromExistUsers = [fromExistUser1, fromExistUser2]; - - const classToClassFromExistUser = classToClassFromExist(users, fromExistUsers); - expect(classToClassFromExistUser[0]).toBeInstanceOf(User); - expect(classToClassFromExistUser[1]).toBeInstanceOf(User); - expect(classToClassFromExistUser[0]).not.toEqual(user1); - expect(classToClassFromExistUser[1]).not.toEqual(user1); - expect(classToClassFromExistUser).toEqual(fromExistUsers); - - const fromExistUserLike1 = new User(); - fromExistUserLike1.id = 1; - fromExistUserLike1.firstName = "Umed"; - fromExistUserLike1.lastName = "Khudoiberdiev"; - - const fromExistUserLike2 = new User(); - fromExistUserLike2.id = 2; - fromExistUserLike2.firstName = "Dima"; - fromExistUserLike2.lastName = "Zotov"; - - expect(classToClassFromExistUser).toEqual([fromExistUserLike1, fromExistUserLike2]); +import 'reflect-metadata'; +import { + classToClass, + classToClassFromExist, + classToPlain, + classToPlainFromExist, + plainToClass, + plainToClassFromExist, +} from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { Exclude, Expose, Type } from '../../src/decorators'; + +describe('specifying target maps', () => { + it('should convert instance of the given object to plain javascript object and should expose all properties since its a default behaviour', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + lastName: string; + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', }); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toBe(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + }); + + it('should exclude all objects marked with @Exclude() decorator', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + lastName: string; + @Exclude() + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27, password: 'yayayaya' }; + const plainUser2 = classToPlainFromExist(user, existUser); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'yayayaya', + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + }); + + it('should exclude all properties from object if whole class is marked with @Exclude() decorator', () => { + defaultMetadataStorage.clear(); + + @Exclude() + class User { + id: number; + firstName: string; + lastName: string; + password: string; + } + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({}); + expect(plainUser.firstName).toBeUndefined(); + expect(plainUser.lastName).toBeUndefined(); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({}); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({}); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + }); + }); + + it('should exclude all properties from object if whole class is marked with @Exclude() decorator, but include properties marked with @Expose() decorator', () => { + defaultMetadataStorage.clear(); + + @Exclude() + class User { + id: number; + + @Expose() + firstName: string; + + @Expose() + lastName: string; + + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + }); + + it('should exclude all properties from object if its defined via transformation options, but include properties marked with @Expose() decorator', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + + @Expose() + firstName: string; + + @Expose() + lastName: string; + + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user, { strategy: 'excludeAll' }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser, { strategy: 'excludeAll' }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser, { strategy: 'excludeAll' }); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: 'excludeAll' }); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassUser = classToClass(user, { strategy: 'excludeAll' }); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: 'excludeAll' }); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + }); + + it('should expose all properties from object if its defined via transformation options, but exclude properties marked with @Exclude() decorator', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + + @Exclude() + lastName: string; + + @Exclude() + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user, { strategy: 'exposeAll' }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + }); + expect(plainUser.lastName).toBeUndefined(); + expect(plainUser.password).toBeUndefined(); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser, { strategy: 'exposeAll' }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser, { strategy: 'exposeAll' }); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: 'Umed', + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: 'exposeAll' }); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + }); + + const classToClassUser = classToClass(user, { strategy: 'exposeAll' }); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: 'exposeAll' }); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + }); + }); + + it('should convert values to specific types if they are set via @Type decorator', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + + @Type(type => String) + firstName: string; + + @Type(type => String) + lastName: string; + + @Type(type => Number) + password: number; + + @Type(type => Boolean) + isActive: boolean; + + @Type(type => Date) + registrationDate: Date; + + @Type(type => String) + lastVisitDate: string; + } + + const date = new Date(); + const user = new User(); + user.firstName = 321 as any; + user.lastName = 123 as any; + user.password = '123' as any; + user.isActive = '1' as any; + user.registrationDate = date.toString() as any; + user.lastVisitDate = date as any; + + const fromPlainUser = { + firstName: 321, + lastName: 123, + password: '123', + isActive: '1', + registrationDate: date.toString(), + lastVisitDate: date, + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + + const plainUser: any = classToPlain(user, { strategy: 'exposeAll' }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + }); + + const existUser = { id: 1, age: 27 }; + const plainUser2 = classToPlainFromExist(user, existUser, { strategy: 'exposeAll' }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + }); + expect(plainUser2).toEqual(existUser); + + const transformedUser = plainToClass(User, fromPlainUser, { strategy: 'exposeAll' }); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser).toEqual({ + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { strategy: 'exposeAll' }); + expect(fromExistTransformedUser).toBeInstanceOf(User); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + }); + + const classToClassUser = classToClass(user, { strategy: 'exposeAll' }); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).toEqual({ + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { strategy: 'exposeAll' }); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: '321', + lastName: '123', + password: 123, + isActive: true, + registrationDate: new Date(date.toString()), + lastVisitDate: date.toString(), + }); + }); + + it('should transform nested objects too and make sure their decorators are used too', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + name: string; + + @Exclude() + filename: string; + + uploadDate: Date; + } + + class User { + firstName: string; + lastName: string; + + @Exclude() + password: string; + + photo: Photo; // type should be automatically guessed + } + + const photo = new Photo(); + photo.id = 1; + photo.name = 'Me'; + photo.filename = 'iam.jpg'; + photo.uploadDate = new Date(); + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = photo; + + const plainUser: any = classToPlain(user, { strategy: 'exposeAll' }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser.photo).not.toBeInstanceOf(Photo); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + uploadDate: photo.uploadDate, + }, + }); + expect(plainUser.password).toBeUndefined(); + expect(plainUser.photo.filename).toBeUndefined(); + expect(plainUser.photo.uploadDate).toEqual(photo.uploadDate); + expect(plainUser.photo.uploadDate).not.toBe(photo.uploadDate); + + const existUser = { id: 1, age: 27, photo: { id: 2, description: 'photo' } }; + const plainUser2: any = classToPlainFromExist(user, existUser, { strategy: 'exposeAll' }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2.photo).not.toBeInstanceOf(Photo); + expect(plainUser2).toEqual({ + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + uploadDate: photo.uploadDate, + description: 'photo', + }, + }); + expect(plainUser2).toEqual(existUser); + expect(plainUser2.password).toBeUndefined(); + expect(plainUser2.photo.filename).toBeUndefined(); + expect(plainUser2.photo.uploadDate).toEqual(photo.uploadDate); + expect(plainUser2.photo.uploadDate).not.toBe(photo.uploadDate); + }); + + it('should transform nested objects too and make sure given type is used instead of automatically guessed one', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + name: string; + + @Exclude() + filename: string; + } + + class ExtendedPhoto implements Photo { + id: number; + + @Exclude() + name: string; + + filename: string; + } + + class User { + id: number; + firstName: string; + lastName: string; + + @Exclude() + password: string; + + @Type(type => ExtendedPhoto) // force specific type + photo: Photo; + } + + const photo = new Photo(); + photo.id = 1; + photo.name = 'Me'; + photo.filename = 'iam.jpg'; + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = photo; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'iam.jpg', + }, + }); + expect(plainUser.password).toBeUndefined(); + expect(plainUser.photo.name).toBeUndefined(); + }); + + it('should convert given plain object to class instance object', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + name: string; + + @Exclude() + filename: string; + + metadata: string; + uploadDate: Date; + } + + class User { + id: number; + firstName: string; + lastName: string; + + @Exclude() + password: string; + + @Type(type => Photo) + photo: Photo; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = new Photo(); + user.photo.id = 1; + user.photo.name = 'Me'; + user.photo.filename = 'iam.jpg'; + user.photo.uploadDate = new Date(); + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + name: 'Me', + filename: 'iam.jpg', + uploadDate: new Date(), + }, + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + const fromExistPhoto = new Photo(); + fromExistPhoto.metadata = 'taken by Camera'; + fromExistUser.photo = fromExistPhoto; + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + expect(transformedUser.photo).toBeInstanceOf(Photo); + expect(transformedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + uploadDate: fromPlainUser.photo.uploadDate, + }, + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser); + expect(fromExistTransformedUser).toEqual(fromExistUser); + expect(fromExistTransformedUser.photo).toEqual(fromExistPhoto); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + metadata: 'taken by Camera', + uploadDate: fromPlainUser.photo.uploadDate, + }, + }); + + const classToClassUser = classToClass(user); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser.photo).toBeInstanceOf(Photo); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).not.toEqual(user.photo); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + uploadDate: user.photo.uploadDate, + }, + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).not.toEqual(user.photo); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + name: 'Me', + metadata: 'taken by Camera', + uploadDate: user.photo.uploadDate, + }, + }); + }); + + it('should expose only properties that match given group', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + + @Expose({ + groups: ['user', 'guest'], + }) + filename: string; + + @Expose({ + groups: ['admin'], + }) + status: number; + + metadata: string; + } + + class User { + id: number; + firstName: string; + + @Expose({ + groups: ['user', 'guest'], + }) + lastName: string; + + @Expose({ + groups: ['user'], + }) + password: string; + + @Expose({ + groups: ['admin'], + }) + isActive: boolean; + + @Type(type => Photo) + photo: Photo; + + @Expose({ + groups: ['admin'], + }) + @Type(type => Photo) + photos: Photo[]; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.isActive = false; + user.photo = new Photo(); + user.photo.id = 1; + user.photo.filename = 'myphoto.jpg'; + user.photo.status = 1; + user.photos = [user.photo]; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + isActive: false, + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }; + + const fromExistUser = new User(); + fromExistUser.id = 1; + fromExistUser.photo = new Photo(); + fromExistUser.photo.metadata = 'taken by Camera'; + + const plainUser1: any = classToPlain(user); + expect(plainUser1).not.toBeInstanceOf(User); + expect(plainUser1).toEqual({ + firstName: 'Umed', + photo: { + id: 1, + }, + }); + expect(plainUser1.lastName).toBeUndefined(); + expect(plainUser1.password).toBeUndefined(); + expect(plainUser1.isActive).toBeUndefined(); + + const plainUser2: any = classToPlain(user, { groups: ['user'] }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + expect(plainUser2.isActive).toBeUndefined(); + + const transformedUser2 = plainToClass(User, fromPlainUser, { groups: ['user'] }); + expect(transformedUser2).toBeInstanceOf(User); + expect(transformedUser2.photo).toBeInstanceOf(Photo); + expect(transformedUser2).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + + const fromExistTransformedUser = plainToClassFromExist(fromExistUser, fromPlainUser, { groups: ['user'] }); + expect(fromExistTransformedUser).toEqual(fromExistUser); + expect(fromExistTransformedUser.photo).toEqual(fromExistUser.photo); + expect(fromExistTransformedUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + metadata: 'taken by Camera', + filename: 'myphoto.jpg', + }, + }); + + const classToClassUser = classToClass(user, { groups: ['user'] }); + expect(classToClassUser).toBeInstanceOf(User); + expect(classToClassUser.photo).toBeInstanceOf(Photo); + expect(classToClassUser).not.toEqual(user); + expect(classToClassUser).not.toEqual(user.photo); + expect(classToClassUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + + const classToClassFromExistUser = classToClassFromExist(user, fromExistUser, { groups: ['user'] }); + expect(classToClassFromExistUser).toBeInstanceOf(User); + expect(classToClassFromExistUser.photo).toBeInstanceOf(Photo); + expect(classToClassFromExistUser).not.toEqual(user); + expect(classToClassFromExistUser).not.toEqual(user.photo); + expect(classToClassFromExistUser).toEqual(fromExistUser); + expect(classToClassFromExistUser).toEqual({ + id: 1, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + metadata: 'taken by Camera', + filename: 'myphoto.jpg', + }, + }); + + const plainUser3: any = classToPlain(user, { groups: ['guest'] }); + expect(plainUser3).not.toBeInstanceOf(User); + expect(plainUser3).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + expect(plainUser3.password).toBeUndefined(); + expect(plainUser3.isActive).toBeUndefined(); + + const transformedUser3 = plainToClass(User, fromPlainUser, { groups: ['guest'] }); + expect(transformedUser3).toBeInstanceOf(User); + expect(transformedUser3.photo).toBeInstanceOf(Photo); + expect(transformedUser3).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + + const plainUser4: any = classToPlain(user, { groups: ['admin'] }); + expect(plainUser4).not.toBeInstanceOf(User); + expect(plainUser4).toEqual({ + firstName: 'Umed', + isActive: false, + photo: { + id: 1, + status: 1, + }, + photos: [ + { + id: 1, + status: 1, + }, + ], + }); + expect(plainUser4.lastName).toBeUndefined(); + expect(plainUser4.password).toBeUndefined(); + + const transformedUser4 = plainToClass(User, fromPlainUser, { groups: ['admin'] }); + expect(transformedUser4).toBeInstanceOf(User); + expect(transformedUser4.photo).toBeInstanceOf(Photo); + expect(transformedUser4.photos[0]).toBeInstanceOf(Photo); + expect(transformedUser4).toEqual({ + firstName: 'Umed', + isActive: false, + photo: { + id: 1, + status: 1, + }, + photos: [ + { + id: 1, + status: 1, + }, + ], + }); + + const plainUser5: any = classToPlain(user, { groups: ['admin', 'user'] }); + expect(plainUser5).not.toBeInstanceOf(User); + expect(plainUser5).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + isActive: false, + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }); + + const transformedUser5 = plainToClass(User, fromPlainUser, { groups: ['admin', 'user'] }); + expect(transformedUser5).toBeInstanceOf(User); + expect(transformedUser5).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + isActive: false, + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }); + }); + + it('should expose only properties that match given version', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + + @Expose({ + since: 1.5, + until: 2, + }) + filename: string; + + @Expose({ + since: 2, + }) + status: number; + } + + class User { + @Expose({ + since: 1, + until: 2, + }) + firstName: string; + + @Expose({ + since: 0.5, + }) + lastName: string; + + @Exclude() + password: string; + + @Type(type => Photo) + photo: Photo; + + @Expose({ + since: 3, + }) + @Type(type => Photo) + photos: Photo[]; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = new Photo(); + user.photo.id = 1; + user.photo.filename = 'myphoto.jpg'; + user.photo.status = 1; + user.photos = [user.photo]; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }; + + const plainUser1: any = classToPlain(user); + expect(plainUser1).not.toBeInstanceOf(User); + expect(plainUser1).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }); + + const transformedUser1 = plainToClass(User, fromPlainUser); + expect(transformedUser1).toBeInstanceOf(User); + expect(transformedUser1.photo).toBeInstanceOf(Photo); + expect(transformedUser1.photos[0]).toBeInstanceOf(Photo); + expect(transformedUser1).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + photos: [ + { + id: 1, + filename: 'myphoto.jpg', + status: 1, + }, + ], + }); + + const plainUser2: any = classToPlain(user, { version: 0.3 }); + expect(plainUser2).not.toBeInstanceOf(User); + expect(plainUser2).toEqual({ + photo: { + id: 1, + }, + }); + + const transformedUser2 = plainToClass(User, fromPlainUser, { version: 0.3 }); + expect(transformedUser2).toBeInstanceOf(User); + expect(transformedUser2.photo).toBeInstanceOf(Photo); + expect(transformedUser2).toEqual({ + photo: { + id: 1, + }, + }); + + const plainUser3: any = classToPlain(user, { version: 0.5 }); + expect(plainUser3).not.toBeInstanceOf(User); + expect(plainUser3).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + }, + }); + + const transformedUser3 = plainToClass(User, fromPlainUser, { version: 0.5 }); + expect(transformedUser3).toBeInstanceOf(User); + expect(transformedUser3.photo).toBeInstanceOf(Photo); + expect(transformedUser3).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + }, + }); + + const plainUser4: any = classToPlain(user, { version: 1 }); + expect(plainUser4).not.toBeInstanceOf(User); + expect(plainUser4).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + }, + }); + + const transformedUser4 = plainToClass(User, fromPlainUser, { version: 1 }); + expect(transformedUser4).toBeInstanceOf(User); + expect(transformedUser4.photo).toBeInstanceOf(Photo); + expect(transformedUser4).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + }, + }); + + const plainUser5: any = classToPlain(user, { version: 1.5 }); + expect(plainUser5).not.toBeInstanceOf(User); + expect(plainUser5).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + + const transformedUser5 = plainToClass(User, fromPlainUser, { version: 1.5 }); + expect(transformedUser5).toBeInstanceOf(User); + expect(transformedUser5.photo).toBeInstanceOf(Photo); + expect(transformedUser5).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + photo: { + id: 1, + filename: 'myphoto.jpg', + }, + }); + + const plainUser6: any = classToPlain(user, { version: 2 }); + expect(plainUser6).not.toBeInstanceOf(User); + expect(plainUser6).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + }); + + const transformedUser6 = plainToClass(User, fromPlainUser, { version: 2 }); + expect(transformedUser6).toBeInstanceOf(User); + expect(transformedUser6.photo).toBeInstanceOf(Photo); + expect(transformedUser6).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + }); + + const plainUser7: any = classToPlain(user, { version: 3 }); + expect(plainUser7).not.toBeInstanceOf(User); + expect(plainUser7).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + photos: [ + { + id: 1, + status: 1, + }, + ], + }); + + const transformedUser7 = plainToClass(User, fromPlainUser, { version: 3 }); + expect(transformedUser7).toBeInstanceOf(User); + expect(transformedUser7.photo).toBeInstanceOf(Photo); + expect(transformedUser7.photos[0]).toBeInstanceOf(Photo); + expect(transformedUser7).toEqual({ + lastName: 'Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + photos: [ + { + id: 1, + status: 1, + }, + ], + }); + }); + + it('should expose method and accessors that have @Expose()', () => { + defaultMetadataStorage.clear(); + + class User { + firstName: string; + lastName: string; + + @Exclude() + password: string; + + @Expose() + get name(): string { + return this.firstName + ' ' + this.lastName; + } + + @Expose() + getName(): string { + return this.firstName + ' ' + this.lastName; + } + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + getName: 'Umed Khudoiberdiev', + }); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + const likeUser = new User(); + likeUser.firstName = 'Umed'; + likeUser.lastName = 'Khudoiberdiev'; + expect(transformedUser).toEqual(likeUser); + }); + + it('should expose with alternative name if its given', () => { + defaultMetadataStorage.clear(); + + class User { + @Expose({ name: 'myName' }) + firstName: string; + + @Expose({ name: 'secondName' }) + lastName: string; + + @Exclude() + password: string; + + @Expose() + get name(): string { + return this.firstName + ' ' + this.lastName; + } + + @Expose({ name: 'fullName' }) + getName(): string { + return this.firstName + ' ' + this.lastName; + } + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const fromPlainUser = { + myName: 'Umed', + secondName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const plainUser: any = classToPlain(user); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + myName: 'Umed', + secondName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + fullName: 'Umed Khudoiberdiev', + }); + + const transformedUser = plainToClass(User, fromPlainUser); + expect(transformedUser).toBeInstanceOf(User); + const likeUser = new User(); + likeUser.firstName = 'Umed'; + likeUser.lastName = 'Khudoiberdiev'; + expect(transformedUser).toEqual(likeUser); + }); + + it('should exclude all prefixed properties if prefix is given', () => { + defaultMetadataStorage.clear(); + + class Photo { + id: number; + $filename: string; + status: number; + } + + class User { + $system: string; + _firstName: string; + _lastName: string; + + @Exclude() + password: string; + + @Type(() => Photo) + photo: Photo; + + @Expose() + get name(): string { + return this._firstName + ' ' + this._lastName; + } + } + + const user = new User(); + user.$system = '@#$%^&*token(*&^%$#@!'; + user._firstName = 'Umed'; + user._lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + user.photo = new Photo(); + user.photo.id = 1; + user.photo.$filename = 'myphoto.jpg'; + user.photo.status = 1; + + const fromPlainUser = { + $system: '@#$%^&*token(*&^%$#@!', + _firstName: 'Khudoiberdiev', + _lastName: 'imnosuperman', + password: 'imnosuperman', + photo: { + id: 1, + $filename: 'myphoto.jpg', + status: 1, + }, + }; + + const plainUser: any = classToPlain(user, { excludePrefixes: ['_', '$'] }); + expect(plainUser).not.toBeInstanceOf(User); + expect(plainUser).toEqual({ + name: 'Umed Khudoiberdiev', + photo: { + id: 1, + status: 1, + }, + }); + + const transformedUser = plainToClass(User, fromPlainUser, { excludePrefixes: ['_', '$'] }); + expect(transformedUser).toBeInstanceOf(User); + const likeUser = new User(); + likeUser.photo = new Photo(); + likeUser.photo.id = 1; + likeUser.photo.status = 1; + expect(transformedUser).toEqual(likeUser); + }); + + it('should be able to transform array too', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + lastName: string; + + @Exclude() + password: string; + + @Expose() + get name(): string { + return this.firstName + ' ' + this.lastName; + } + } + + const user1 = new User(); + user1.firstName = 'Umed'; + user1.lastName = 'Khudoiberdiev'; + user1.password = 'imnosuperman'; + + const user2 = new User(); + user2.firstName = 'Dima'; + user2.lastName = 'Zotov'; + user2.password = 'imnomesser'; + + const users = [user1, user2]; + + const plainUsers: any = classToPlain(users); + expect(plainUsers).toEqual([ + { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + }, + { + firstName: 'Dima', + lastName: 'Zotov', + name: 'Dima Zotov', + }, + ]); + + const fromPlainUsers = [ + { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + }, + { + firstName: 'Dima', + lastName: 'Zotov', + name: 'Dima Zotov', + }, + ]; + + const existUsers = [ + { id: 1, age: 27 }, + { id: 2, age: 30 }, + ]; + const plainUser2 = classToPlainFromExist(users, existUsers); + expect(plainUser2).toEqual([ + { + id: 1, + age: 27, + firstName: 'Umed', + lastName: 'Khudoiberdiev', + name: 'Umed Khudoiberdiev', + }, + { + id: 2, + age: 30, + firstName: 'Dima', + lastName: 'Zotov', + name: 'Dima Zotov', + }, + ]); + + const transformedUser = plainToClass(User, fromPlainUsers); + + expect(transformedUser[0]).toBeInstanceOf(User); + expect(transformedUser[1]).toBeInstanceOf(User); + const likeUser1 = new User(); + likeUser1.firstName = 'Umed'; + likeUser1.lastName = 'Khudoiberdiev'; + + const likeUser2 = new User(); + likeUser2.firstName = 'Dima'; + likeUser2.lastName = 'Zotov'; + expect(transformedUser).toEqual([likeUser1, likeUser2]); + + const classToClassUsers = classToClass(users); + expect(classToClassUsers[0]).toBeInstanceOf(User); + expect(classToClassUsers[1]).toBeInstanceOf(User); + expect(classToClassUsers[0]).not.toEqual(user1); + expect(classToClassUsers[1]).not.toEqual(user1); + + const classUserLike1 = new User(); + classUserLike1.firstName = 'Umed'; + classUserLike1.lastName = 'Khudoiberdiev'; + + const classUserLike2 = new User(); + classUserLike2.firstName = 'Dima'; + classUserLike2.lastName = 'Zotov'; + + expect(classToClassUsers).toEqual([classUserLike1, classUserLike2]); + + const fromExistUser1 = new User(); + fromExistUser1.id = 1; + + const fromExistUser2 = new User(); + fromExistUser2.id = 2; + + const fromExistUsers = [fromExistUser1, fromExistUser2]; + + const classToClassFromExistUser = classToClassFromExist(users, fromExistUsers); + expect(classToClassFromExistUser[0]).toBeInstanceOf(User); + expect(classToClassFromExistUser[1]).toBeInstanceOf(User); + expect(classToClassFromExistUser[0]).not.toEqual(user1); + expect(classToClassFromExistUser[1]).not.toEqual(user1); + expect(classToClassFromExistUser).toEqual(fromExistUsers); + + const fromExistUserLike1 = new User(); + fromExistUserLike1.id = 1; + fromExistUserLike1.firstName = 'Umed'; + fromExistUserLike1.lastName = 'Khudoiberdiev'; + + const fromExistUserLike2 = new User(); + fromExistUserLike2.id = 2; + fromExistUserLike2.firstName = 'Dima'; + fromExistUserLike2.lastName = 'Zotov'; + + expect(classToClassFromExistUser).toEqual([fromExistUserLike1, fromExistUserLike2]); + }); }); diff --git a/test/functional/transformation-option.spec.ts b/test/functional/transformation-option.spec.ts index ed70a1325..70dcca04f 100644 --- a/test/functional/transformation-option.spec.ts +++ b/test/functional/transformation-option.spec.ts @@ -1,164 +1,164 @@ -import "reflect-metadata"; -import {classToPlain, plainToClass} from "../../src/index"; -import {defaultMetadataStorage} from "../../src/storage"; -import {Exclude, Expose} from "../../src/decorators"; - -describe("filtering by transformation option", () => { - it("@Exclude with toPlainOnly set to true then it should be excluded only during classToPlain and classToPlainFromExist operations", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - lastName: string; - - @Exclude({ toPlainOnly: true }) - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const plainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const plainedUser = classToPlain(user); - expect(plainedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classedUser = plainToClass(User, plainUser); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); +import 'reflect-metadata'; +import { classToPlain, plainToClass } from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { Exclude, Expose } from '../../src/decorators'; + +describe('filtering by transformation option', () => { + it('@Exclude with toPlainOnly set to true then it should be excluded only during classToPlain and classToPlainFromExist operations', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + lastName: string; + + @Exclude({ toPlainOnly: true }) + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const plainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const plainedUser = classToPlain(user); + expect(plainedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', }); - it("@Exclude with toClassOnly set to true then it should be excluded only during plainToClass and plainToClassFromExist operations", () => { - defaultMetadataStorage.clear(); - - class User { - id: number; - firstName: string; - lastName: string; - - @Exclude({ toClassOnly: true }) - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const plainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const classedUser = plainToClass(User, plainUser); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const plainedUser = classToPlain(user); - expect(plainedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); + const classedUser = plainToClass(User, plainUser); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + }); + + it('@Exclude with toClassOnly set to true then it should be excluded only during plainToClass and plainToClassFromExist operations', () => { + defaultMetadataStorage.clear(); + + class User { + id: number; + firstName: string; + lastName: string; + + @Exclude({ toClassOnly: true }) + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const plainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const classedUser = plainToClass(User, plainUser); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', }); - it("@Expose with toClassOnly set to true then it should be excluded only during classToPlain and classToPlainFromExist operations", () => { - defaultMetadataStorage.clear(); - - @Exclude() - class User { - @Expose() - firstName: string; - - @Expose() - lastName: string; - - @Expose({ toClassOnly: true }) - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const plainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const plainedUser = classToPlain(user); - expect(plainedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); - - const classedUser = plainToClass(User, plainUser); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); + const plainedUser = classToPlain(user); + expect(plainedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + }); + + it('@Expose with toClassOnly set to true then it should be excluded only during classToPlain and classToPlainFromExist operations', () => { + defaultMetadataStorage.clear(); + + @Exclude() + class User { + @Expose() + firstName: string; + + @Expose() + lastName: string; + + @Expose({ toClassOnly: true }) + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const plainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const plainedUser = classToPlain(user); + expect(plainedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + }); + + const classedUser = plainToClass(User, plainUser); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }); + }); + + it('@Expose with toPlainOnly set to true then it should be excluded only during classToPlain and classToPlainFromExist operations', () => { + defaultMetadataStorage.clear(); + + @Exclude() + class User { + @Expose() + firstName: string; + + @Expose() + lastName: string; + + @Expose({ toPlainOnly: true }) + password: string; + } + + const user = new User(); + user.firstName = 'Umed'; + user.lastName = 'Khudoiberdiev'; + user.password = 'imnosuperman'; + + const plainUser = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', + }; + + const plainedUser = classToPlain(user); + expect(plainedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + password: 'imnosuperman', }); - it("@Expose with toPlainOnly set to true then it should be excluded only during classToPlain and classToPlainFromExist operations", () => { - defaultMetadataStorage.clear(); - - @Exclude() - class User { - @Expose() - firstName: string; - - @Expose() - lastName: string; - - @Expose({ toPlainOnly: true }) - password: string; - } - - const user = new User(); - user.firstName = "Umed"; - user.lastName = "Khudoiberdiev"; - user.password = "imnosuperman"; - - const plainUser = { - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }; - - const plainedUser = classToPlain(user); - expect(plainedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev", - password: "imnosuperman" - }); - - const classedUser = plainToClass(User, plainUser); - expect(classedUser).toBeInstanceOf(User); - expect(classedUser).toEqual({ - firstName: "Umed", - lastName: "Khudoiberdiev" - }); + const classedUser = plainToClass(User, plainUser); + expect(classedUser).toBeInstanceOf(User); + expect(classedUser).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', }); + }); }); diff --git a/test/functional/transformer-method.spec.ts b/test/functional/transformer-method.spec.ts index b5e0aff2a..2d0bc0e07 100644 --- a/test/functional/transformer-method.spec.ts +++ b/test/functional/transformer-method.spec.ts @@ -1,262 +1,256 @@ -import "reflect-metadata"; -import {defaultMetadataStorage} from "../../src/storage"; -import {Exclude, Expose, TransformClassToClass, TransformClassToPlain, TransformPlainToClass} from "../../src/decorators"; - -describe("transformer methods decorator", () => { - it("should expose non configuration properties and return User instance class", () => { - defaultMetadataStorage.clear(); - - @Exclude() - class User { - id: number; - - @Expose() - firstName: string; - - @Expose() - lastName: string; - - password: string; - } - - class UserController { - @TransformClassToClass() - getUser(): User { - const user = new User(); - user.firstName = "Snir"; - user.lastName = "Segal"; - user.password = "imnosuperman"; - - return user; - } - } - - const controller = new UserController(); - - const result = controller.getUser(); - expect(result.password).toBeUndefined(); - - const plainUser = { - firstName: "Snir", - lastName: "Segal" - }; - - - expect(result).toEqual(plainUser); - expect(result).toBeInstanceOf(User); - }); - - it("should expose non configuration properties and return User instance class instead of plain object", () => { - defaultMetadataStorage.clear(); - - @Exclude() - class User { - - id: number; - - @Expose() - firstName: string; - - @Expose() - lastName: string; - - password: string; - } - - class UserController { - - @TransformPlainToClass(User) - getUser(): User { - const user: any = {}; - user.firstName = "Snir"; - user.lastName = "Segal"; - user.password = "imnosuperman"; - - return user; - } - } - - const controller = new UserController(); - - const result = controller.getUser(); - expect(result.password).toBeUndefined(); - +import 'reflect-metadata'; +import { defaultMetadataStorage } from '../../src/storage'; +import { + Exclude, + Expose, + TransformClassToClass, + TransformClassToPlain, + TransformPlainToClass, +} from '../../src/decorators'; + +describe('transformer methods decorator', () => { + it('should expose non configuration properties and return User instance class', () => { + defaultMetadataStorage.clear(); + + @Exclude() + class User { + id: number; + + @Expose() + firstName: string; + + @Expose() + lastName: string; + + password: string; + } + + class UserController { + @TransformClassToClass() + getUser(): User { const user = new User(); - user.firstName = "Snir"; - user.lastName = "Segal"; - - expect(result).toEqual(user); - expect(result).toBeInstanceOf(User); - }); + user.firstName = 'Snir'; + user.lastName = 'Segal'; + user.password = 'imnosuperman'; - it("should expose non configuration properties", () => { - defaultMetadataStorage.clear(); + return user; + } + } - @Exclude() - class User { + const controller = new UserController(); - id: number; + const result = controller.getUser(); + expect(result.password).toBeUndefined(); - @Expose() - firstName: string; + const plainUser = { + firstName: 'Snir', + lastName: 'Segal', + }; - @Expose() - lastName: string; + expect(result).toEqual(plainUser); + expect(result).toBeInstanceOf(User); + }); - password: string; - } + it('should expose non configuration properties and return User instance class instead of plain object', () => { + defaultMetadataStorage.clear(); - class UserController { + @Exclude() + class User { + id: number; - @TransformClassToPlain() - getUser(): User { - const user = new User(); - user.firstName = "Snir"; - user.lastName = "Segal"; - user.password = "imnosuperman"; + @Expose() + firstName: string; - return user; - } - } + @Expose() + lastName: string; - const controller = new UserController(); + password: string; + } - const result = controller.getUser(); - expect(result.password).toBeUndefined(); + class UserController { + @TransformPlainToClass(User) + getUser(): User { + const user: any = {}; + user.firstName = 'Snir'; + user.lastName = 'Segal'; + user.password = 'imnosuperman'; - const plainUser = { - firstName: "Snir", - lastName: "Segal" - }; + return user; + } + } - expect(result).toEqual(plainUser); - }); + const controller = new UserController(); - it("should expose non configuration properties and properties with specific groups", () => { - defaultMetadataStorage.clear(); + const result = controller.getUser(); + expect(result.password).toBeUndefined(); - @Exclude() - class User { + const user = new User(); + user.firstName = 'Snir'; + user.lastName = 'Segal'; - id: number; + expect(result).toEqual(user); + expect(result).toBeInstanceOf(User); + }); - @Expose() - firstName: string; + it('should expose non configuration properties', () => { + defaultMetadataStorage.clear(); - @Expose() - lastName: string; + @Exclude() + class User { + id: number; - @Expose({ groups: ["user.permissions"] }) - roles: string[]; + @Expose() + firstName: string; - password: string; - } + @Expose() + lastName: string; - class UserController { + password: string; + } - @TransformClassToPlain({ groups: ["user.permissions"] }) - getUserWithRoles(): User { - const user = new User(); - user.firstName = "Snir"; - user.lastName = "Segal"; - user.password = "imnosuperman"; - user.roles = ["USER", "MANAGER"]; - - return user; - } + class UserController { + @TransformClassToPlain() + getUser(): User { + const user = new User(); + user.firstName = 'Snir'; + user.lastName = 'Segal'; + user.password = 'imnosuperman'; - } + return user; + } + } - const controller = new UserController(); + const controller = new UserController(); - const result = controller.getUserWithRoles(); - expect(result.password).toBeUndefined(); + const result = controller.getUser(); + expect(result.password).toBeUndefined(); - const plainUser = { - firstName: "Snir", - lastName: "Segal", - roles: ["USER", "MANAGER"] - }; + const plainUser = { + firstName: 'Snir', + lastName: 'Segal', + }; - expect(result).toEqual(plainUser); - }); + expect(result).toEqual(plainUser); + }); - it("should expose non configuration properties with specific version", () => { - defaultMetadataStorage.clear(); + it('should expose non configuration properties and properties with specific groups', () => { + defaultMetadataStorage.clear(); - @Exclude() - class User { + @Exclude() + class User { + id: number; - id: number; + @Expose() + firstName: string; - @Expose() - firstName: string; + @Expose() + lastName: string; - @Expose() - lastName: string; + @Expose({ groups: ['user.permissions'] }) + roles: string[]; - @Expose({ groups: ["user.permissions"] }) - roles: string[]; + password: string; + } - @Expose({ since: 2 }) - websiteUrl?: string; + class UserController { + @TransformClassToPlain({ groups: ['user.permissions'] }) + getUserWithRoles(): User { + const user = new User(); + user.firstName = 'Snir'; + user.lastName = 'Segal'; + user.password = 'imnosuperman'; + user.roles = ['USER', 'MANAGER']; - password: string; - } + return user; + } + } - class UserController { + const controller = new UserController(); - @TransformClassToPlain({ version: 1 }) - getUserVersion1(): User { - const user = new User(); - user.firstName = "Snir"; - user.lastName = "Segal"; - user.password = "imnosuperman"; - user.roles = ["USER", "MANAGER"]; - user.websiteUrl = "http://www.github.com"; + const result = controller.getUserWithRoles(); + expect(result.password).toBeUndefined(); - return user; - } + const plainUser = { + firstName: 'Snir', + lastName: 'Segal', + roles: ['USER', 'MANAGER'], + }; - @TransformClassToPlain({ version: 2 }) - getUserVersion2(): User { - const user = new User(); - user.firstName = "Snir"; - user.lastName = "Segal"; - user.password = "imnosuperman"; - user.roles = ["USER", "MANAGER"]; - user.websiteUrl = "http://www.github.com"; + expect(result).toEqual(plainUser); + }); - return user; - } + it('should expose non configuration properties with specific version', () => { + defaultMetadataStorage.clear(); - } + @Exclude() + class User { + id: number; - const controller = new UserController(); + @Expose() + firstName: string; - const resultV2 = controller.getUserVersion2(); - expect(resultV2.password).toBeUndefined(); - expect(resultV2.roles).toBeUndefined(); + @Expose() + lastName: string; - const plainUserV2 = { - firstName: "Snir", - lastName: "Segal", - websiteUrl: "http://www.github.com" - }; + @Expose({ groups: ['user.permissions'] }) + roles: string[]; - expect(resultV2).toEqual(plainUserV2); + @Expose({ since: 2 }) + websiteUrl?: string; - const resultV1 = controller.getUserVersion1(); - expect(resultV1.password).toBeUndefined(); - expect(resultV1.roles).toBeUndefined(); - expect(resultV1.websiteUrl).toBeUndefined(); + password: string; + } - const plainUserV1 = { - firstName: "Snir", - lastName: "Segal" - }; + class UserController { + @TransformClassToPlain({ version: 1 }) + getUserVersion1(): User { + const user = new User(); + user.firstName = 'Snir'; + user.lastName = 'Segal'; + user.password = 'imnosuperman'; + user.roles = ['USER', 'MANAGER']; + user.websiteUrl = 'http://www.github.com'; - expect(resultV1).toEqual(plainUserV1); - }); + return user; + } + @TransformClassToPlain({ version: 2 }) + getUserVersion2(): User { + const user = new User(); + user.firstName = 'Snir'; + user.lastName = 'Segal'; + user.password = 'imnosuperman'; + user.roles = ['USER', 'MANAGER']; + user.websiteUrl = 'http://www.github.com'; + + return user; + } + } + + const controller = new UserController(); + + const resultV2 = controller.getUserVersion2(); + expect(resultV2.password).toBeUndefined(); + expect(resultV2.roles).toBeUndefined(); + + const plainUserV2 = { + firstName: 'Snir', + lastName: 'Segal', + websiteUrl: 'http://www.github.com', + }; + + expect(resultV2).toEqual(plainUserV2); + + const resultV1 = controller.getUserVersion1(); + expect(resultV1.password).toBeUndefined(); + expect(resultV1.roles).toBeUndefined(); + expect(resultV1.websiteUrl).toBeUndefined(); + + const plainUserV1 = { + firstName: 'Snir', + lastName: 'Segal', + }; + + expect(resultV1).toEqual(plainUserV1); + }); }); diff --git a/test/functional/transformer-order.spec.ts b/test/functional/transformer-order.spec.ts index 1e79df00d..312c587e5 100644 --- a/test/functional/transformer-order.spec.ts +++ b/test/functional/transformer-order.spec.ts @@ -1,28 +1,28 @@ -import "reflect-metadata"; -import { plainToClass } from "../../src/index"; -import { defaultMetadataStorage } from "../../src/storage"; -import { Expose, Transform } from "../../src/decorators"; +import 'reflect-metadata'; +import { plainToClass } from '../../src/index'; +import { defaultMetadataStorage } from '../../src/storage'; +import { Expose, Transform } from '../../src/decorators'; -describe("applying several transformations", () => { +describe('applying several transformations', () => { beforeEach(() => defaultMetadataStorage.clear()); afterEach(() => defaultMetadataStorage.clear()); - it("should keep the order of the applied decorators after several plainToClass() calls", () => { + it('should keep the order of the applied decorators after several plainToClass() calls', () => { class User { - @Transform(() => "Jonathan") - @Transform(() => "John") + @Transform(() => 'Jonathan') + @Transform(() => 'John') @Expose() name: string; } - const firstUser = plainToClass(User, { name: "Joe" }); - expect(firstUser.name).toEqual("John"); + const firstUser = plainToClass(User, { name: 'Joe' }); + expect(firstUser.name).toEqual('John'); // Prior to this pull request [#355](https://github.com/typestack/class-transformer/pull/355) // the order of the transformations was reversed after every `plainToClass()` call // So after consecutive calls `User#name` would be "John" - "Jonathan" - "John" - "Jonathan"... // This test ensures the last transformation is always the last one to be applied - const secondUser = plainToClass(User, { name: "Joe" }); - expect(secondUser.name).toEqual("John"); + const secondUser = plainToClass(User, { name: 'Joe' }); + expect(secondUser.name).toEqual('John'); }); });