From ad8fb42cb7c50b13e4e3b56055fa338375335389 Mon Sep 17 00:00:00 2001 From: "Marc J. Schmidt" Date: Fri, 20 Dec 2019 20:30:34 +0100 Subject: [PATCH] removed @Field everywhere --- packages/benchmark/bench-big.spec.ts | 22 +- packages/benchmark/bench.spec.ts | 6 +- packages/core/src/decorators.ts | 287 ++++++------------ packages/core/src/validation.ts | 11 - packages/core/tests/big-entity.ts | 229 +++++++------- packages/core/tests/decorator.spec.ts | 156 ++-------- packages/core/tests/decorator2.spec.ts | 145 +++++++-- .../tests/document-scenario/DocumentClass.ts | 20 +- .../core/tests/document-scenario/PageClass.ts | 14 +- .../tests/document-scenario/PageCollection.ts | 5 +- packages/core/tests/entities.ts | 80 ++--- packages/core/tests/method-decorators.spec.ts | 55 +++- packages/core/tests/plain-to.spec.ts | 17 +- packages/core/tests/validation.spec.ts | 273 +++++++++++------ packages/core/tests/validation2.spec.ts | 36 +-- packages/mongo/tests/mongo-query.spec.ts | 13 +- packages/mongo/tests/mongo.spec.ts | 24 +- packages/mongo/tests/to-class.spec.ts | 48 ++- packages/mongo/tests/to-mongo.spec.ts | 26 +- packages/mongo/tests/to-plain.spec.ts | 14 +- packages/mongo/tests/typeorm.spec.ts | 16 +- packages/nest/tests/validation.spec.ts | 14 +- 22 files changed, 731 insertions(+), 780 deletions(-) diff --git a/packages/benchmark/bench-big.spec.ts b/packages/benchmark/bench-big.spec.ts index a243ebc70..efa48a1ad 100644 --- a/packages/benchmark/bench-big.spec.ts +++ b/packages/benchmark/bench-big.spec.ts @@ -1,41 +1,41 @@ import 'jest'; import 'reflect-metadata'; -import {classToPlain, EnumField, Exclude, Field, plainToClass} from "../core"; +import {classToPlain, f, plainToClass} from "../core"; import {plainToClass as classTransformerPlainToClass, classToPlain as classTransformerClassToPlain, Exclude as ctExclude, Transform, Type} from "class-transformer"; import {bench} from "./util"; import {Plan, SubModel} from "@marcj/marshal/tests/entities"; export class SimpleModel { - @Field() + @f name: string; - @Field() + @f type: number = 0; - @Field() + @f yesNo: boolean = false; - @EnumField(Plan) + @f.enum(Plan) plan: Plan = Plan.DEFAULT; - @Field() + @f created: Date = new Date; - @Field([String]) + @f.array(String) types: string[] = []; - @Field([SubModel]) + @f.array(SubModel) children: SubModel[] = []; - @Field({SubModel}) + @f.map(SubModel) childrenMap: { [key: string]: SubModel } = {}; notMapped: { [key: string]: any } = {}; - @Field().exclude() + @f.exclude() excluded: string = 'default'; - @Field().exclude('plain') + @f.exclude('plain') excludedForPlain: string = 'excludedForPlain'; constructor(name: string) { diff --git a/packages/benchmark/bench.spec.ts b/packages/benchmark/bench.spec.ts index a018af41c..8a69ddc52 100644 --- a/packages/benchmark/bench.spec.ts +++ b/packages/benchmark/bench.spec.ts @@ -1,14 +1,14 @@ import 'jest'; import 'reflect-metadata'; -import {classToPlain, Field, plainToClass} from "../core"; +import {classToPlain, f, plainToClass} from "../core"; import {plainToClass as classTransformerPlainToClass, classToPlain as classTransformerClassToPlain} from "class-transformer"; import {bench} from "./util"; export class MarshalSuperSimple { constructor( - @Field() public id: number, - @Field() public name: string + @f public id: number, + @f public name: string ) {} } diff --git a/packages/core/src/decorators.ts b/packages/core/src/decorators.ts index 7a8e9b059..a8eb1cf78 100644 --- a/packages/core/src/decorators.ts +++ b/packages/core/src/decorators.ts @@ -1,24 +1,14 @@ -import {getCachedParameterNames, Types} from "./mapper"; +import {Types} from "./mapper"; import { BooleanValidator, DateValidator, NumberValidator, ObjectIdValidator, - Optional, PropertyValidatorError, StringValidator, UUIDValidator } from "./validation"; -import { - ClassType, - getClassName, - isArray, - isNumber, - isObject, - isPlainObject, - eachPair, - eachKey -} from '@marcj/estdlib'; +import {ClassType, eachKey, eachPair, getClassName, isArray, isNumber, isObject, isPlainObject} from '@marcj/estdlib'; import {Buffer} from "buffer"; import * as getParameterNames from "get-parameter-names"; @@ -157,7 +147,7 @@ export class PropertySchema { } if (!this.classType) { - throw new Error(`No classType given for ${this.name}. Use @Field(forwardRef(() => MyClass)) for circular dependencies.`); + throw new Error(`No classType given for ${this.name}. Use @f.forward(() => MyClass) for circular dependencies.`); } return this.classType; @@ -181,6 +171,7 @@ export class ClassSchema { * Each method can have its own PropertySchema definition for each argument, where map key = method name. */ methodProperties = new Map(); + normalizedMethodProperties: {[name: string]: true} = {}; classProperties: { [name: string]: PropertySchema } = {}; @@ -246,7 +237,21 @@ export class ClassSchema { * array item. */ public getMethodProperties(name: string): PropertySchema[] { - return this.methodProperties.get(name) || []; + const properties = this.getOrCreateMethodProperties(name); + if (!this.normalizedMethodProperties[name]) { + for (const [i, p] of eachPair(properties)) { + if (!p) { + properties[i] = new PropertySchema(String(i)); + const returnTypes = Reflect.getMetadata('design:paramtypes', this.proto, name); + if (returnTypes) { + properties[i].type = returnTypes + } + } + } + this.normalizedMethodProperties[name] = true; + } + + return properties; } /** @@ -470,15 +475,21 @@ export interface FieldDecoratorResult { index(options?: IndexOptions, name?: string): FieldDecoratorResult; /** + * Mongo's ObjectID. * @see MongoIdField */ - mongo(): FieldDecoratorResult; + mongoId(): FieldDecoratorResult; /** * @see UUIDField */ uuid(): FieldDecoratorResult; + /** + * @see Decorated + */ + decorated(): FieldDecoratorResult; + /** * @see FieldArray */ @@ -503,7 +514,19 @@ function createFieldDecoratorResult( modifiedOptions: FieldOptions = {}, root = false, ): FieldDecoratorResult { + function resetIfNecessary() { + //on root we never use the overwritten name, so we set it back + //for child FieldDecoratorResults created via asName() etc we keep that stuff (since there is root=false) + if (root) { + givenPropertyName = ''; + modifier = []; + modifiedOptions = {}; + } + } + const fn = (target: Object, propertyOrMethodName?: string, parameterIndexOrDescriptor?: any) => { + resetIfNecessary(); + let returnType; let methodName = 'constructor'; const schema = getOrCreateEntitySchema(target); @@ -621,53 +644,61 @@ function createFieldDecoratorResult( } cb(target, propertySchema!, returnType, {...modifiedOptions}); - - //when @f is used alone, we need to reset the roots state - if (root) { - givenPropertyName = ''; - modifier = []; - modifiedOptions = {}; - } }; fn.asName = (name: string) => { + resetIfNecessary(); return createFieldDecoratorResult(cb, name, [...modifier, Optional()], modifiedOptions); }; fn.optional = () => { + resetIfNecessary(); return createFieldDecoratorResult(cb, givenPropertyName, [...modifier, Optional()], modifiedOptions); }; fn.exclude = (target: 'all' | 'mongo' | 'plain' = 'all') => { + resetIfNecessary(); return createFieldDecoratorResult(cb, givenPropertyName, [...modifier, Exclude(target)], modifiedOptions); }; fn.id = fn.asId = () => { + resetIfNecessary(); return createFieldDecoratorResult(cb, givenPropertyName, [...modifier, IDField()], modifiedOptions); }; fn.index = (options?: IndexOptions, name?: string) => { + resetIfNecessary(); return createFieldDecoratorResult(cb, givenPropertyName, [...modifier, Index(options, name)], modifiedOptions); }; - fn.mongo = () => { + fn.mongoId = () => { + resetIfNecessary(); return createFieldDecoratorResult(cb, givenPropertyName, [...modifier, MongoIdField()], modifiedOptions); }; fn.uuid = () => { + resetIfNecessary(); return createFieldDecoratorResult(cb, givenPropertyName, [...modifier, UUIDField()], modifiedOptions); }; + fn.decorated = () => { + resetIfNecessary(); + return createFieldDecoratorResult(cb, givenPropertyName, [...modifier, Decorated()], modifiedOptions); + }; + fn.use = (decorator: (target: Object, propertyOrMethodName?: string, parameterIndexOrDescriptor?: any) => void) => { + resetIfNecessary(); return createFieldDecoratorResult(cb, givenPropertyName, [...modifier, decorator], modifiedOptions); }; fn.asArray = () => { + resetIfNecessary(); if (modifiedOptions.map) throw new Error('Field is already defined as map.'); return createFieldDecoratorResult(cb, givenPropertyName, modifier, {...modifiedOptions, array: true}); }; fn.asMap = () => { + resetIfNecessary(); if (modifiedOptions.array) throw new Error('Field is already defined as array.'); return createFieldDecoratorResult(cb, givenPropertyName, modifier, {...modifiedOptions, map: true}); }; @@ -680,9 +711,10 @@ function createFieldDecoratorResult( * We detect the name by reading the constructor' signature, which would be otherwise lost. */ export function FieldDecoratorWrapper( - cb: (target: Object, property: PropertySchema, returnType: any, modifiedOptions: FieldOptions) => void + cb: (target: Object, property: PropertySchema, returnType: any, modifiedOptions: FieldOptions) => void, + root = false ): FieldDecoratorResult { - return createFieldDecoratorResult(cb, '', [], {}, true); + return createFieldDecoratorResult(cb, '', [], {}, root); } /** @@ -693,13 +725,10 @@ export function FieldDecoratorWrapper( * * Only one field per class can be the decorated one. * - * @category Decorator - * * Example * ```typescript * export class PageCollection { - * @Field(() => PageClass) - * @Decorated() + * @f.forward(() => PageClass).decorated() * private readonly pages: PageClass[] = []; * * constructor(pages: PageClass[] = []) { @@ -716,13 +745,13 @@ export function FieldDecoratorWrapper( * } * * export class PageClass { - * @UUIDField() + * @f.uuid() * id: string = uuid(); * - * @Field() + * @f * name: string; * - * @ClassCircular(() => PageCollection) + * @f.forward(() => PageCollection) * children: PageCollection = new PageCollection; * * constructor(name: string) { @@ -734,7 +763,7 @@ export function FieldDecoratorWrapper( * If you use classToPlain(PageClass, ...) or classToMongo(PageClass, ...) the field value of `children` will be the type of * `PageCollection.pages` (always the field where @Decorated() is applied to), here a array of PagesClass `PageClass[]`. */ -export function Decorated() { +function Decorated() { return FieldDecoratorWrapper((target, property) => { getOrCreateEntitySchema(target).decorator = property.name; property.isDecorated = true; @@ -747,16 +776,23 @@ export function Decorated() { * This is important if you interact with the database abstraction. * * Only one field can be the ID. - * - * @category Decorator */ -export function IDField() { +function IDField() { return FieldDecoratorWrapper((target, property) => { getOrCreateEntitySchema(target).idField = property.name; property.isId = true; }); } +/** + * Used to mark a field as optional. The validation requires field values per default, this makes it optional. + */ +function Optional() { + return FieldDecoratorWrapper((target, property) => { + property.isOptional = true; + }); +} + /** * Used to define a field as a reference to a parent. * @@ -765,7 +801,7 @@ export function IDField() { * Example one direction. * ```typescript * class JobConfig { - * @Type(() => Job) //necessary since circular dependency + * @f.forward(() => Job) //forward necessary since circular dependency * @ParentReference() * job: Job; * @@ -780,16 +816,16 @@ export function IDField() { * Example circular parent-child setup. * ```typescript * export class PageClass { - * @UUIDField() + * @f.uuid() * id: string = uuid(); * - * @Field() + * @f * name: string; * - * @Field(() => PageClass).asArray() + * @f.forwardArray(() => PageClass) //forward necessary since circular dependency * children: Page[] = []; * - * @Field(() => PageClass).optional() + * @f.forward(() => PageClass).optional() //forward necessary since circular dependency * @ParentReference() * parent?: PageClass; * @@ -812,7 +848,6 @@ export function ParentReference() { * Example * ```typescript * class User { - * * @OnLoad() * onLoad() { * console.log('self loaded!'); @@ -841,10 +876,8 @@ export function OnLoad(options: { fullLoad?: boolean } = {}) { /** * Used to define a field as excluded when serialized from class to different targets (currently to Mongo or JSON). * PlainToClass or mongoToClass is not effected by this. - * - * @category Decorator */ -export function Exclude(t: 'all' | 'mongo' | 'plain' = 'all') { +function Exclude(t: 'all' | 'mongo' | 'plain' = 'all') { return FieldDecoratorWrapper((target, property) => { property.exclude = t; }); @@ -929,58 +962,6 @@ interface FieldOptions { /** * Decorator to define a field for an entity. - * - * ```typescript - * class User { - * @Field().optional() - * num?: number; - * - * @Field().optional() - * birthdate?: Date; - * - * @Field().optional() - * yes?: Boolean; - * - * @Field([String]) - * //or ... - * @Field(String).asArray() - * tags: string[] = []; - * - * @Field({String}) - * //or ... - * @Field(String).asMap() - * flatStringConfigs: {[name: string]: String} = {}}; - * - * @FieldAny({}) - * //or ... - * @Field({}).asMap() - * flatConfig: {[name: string]: any} = {}}; - * - * @Field(MyClass) - * config: MyClass = new wMyClass; - * - * @Field([MyClass]) - * //or ... - * @Field(MyClass).asArray() - * configArray: MyClass[] = []]; - * - * @Field({MyClass}) - * //or ... - * @Field(MyClass).asMap() - * configMap: {[name: string]: MyClass} = {}}; - * - * constrcutor( - * @Field().index({unique: true}) - * //if minified using bundled and names are removed, use asName() as as first argument - * //the exact same property name as string. During compilation/bundling this information - * //is otherwise lost and transformations fails. - * @Field().asName('name').index({unique: true}) - * public name: string - * ) {} - * } - * ``` - * - * @category Decorator */ export function Field(oriType?: FieldTypes | FieldTypes[] | { [n: string]: FieldTypes }) { return FieldDecoratorWrapper((target, property, returnType, options) => { @@ -1047,9 +1028,10 @@ export function Field(oriType?: FieldTypes | FieldTypes[] | { [n: string]: Field } if (!type && returnType === Object) { + //typescript puts `Object` for undefined types. throw new Error(`${id} type mismatch. Given ${getTypeDeclaration(type, options)}, but declared is Object or undefined. ` + `When you don't declare a type in TypeScript or types are excluded, you need to pass a type manually via @f.type(String).\n` + - `If you don't have a type, use @FieldAny(). If you reference a class with circular dependency, use @f.forward(() => MyType).` + `If you don't have a type, use @f.any(). If you reference a class with circular dependency, use @f.forward(() => MyType).` ); } @@ -1124,7 +1106,7 @@ export function Field(oriType?: FieldTypes | FieldTypes[] | { [n: string]: Field if (property.type === 'any') { property.type = options.type!; } - }); + }, true); } declare type TYPES = FieldTypes | FieldTypes[] | { [n: string]: FieldTypes }; @@ -1139,10 +1121,18 @@ fRaw['map'] = function (this: FieldDecoratorResult, type: TYPES): FieldDecorator return Field(type).asMap(); }; +fRaw['any'] = function (this: FieldDecoratorResult): FieldDecoratorResult { + return Field(Any); +}; + fRaw['type'] = function (this: FieldDecoratorResult, type: TYPES): FieldDecoratorResult { return Field(type); }; +fRaw['enum'] = function (this: FieldDecoratorResult, clazz: any, allowLabelsAsValue = false): FieldDecoratorResult { + return EnumField(clazz, allowLabelsAsValue); +}; + fRaw['forward'] = function (this: FieldDecoratorResult, f: () => TYPES): FieldDecoratorResult { return Field(forwardRef(f)); }; @@ -1161,96 +1151,19 @@ fRaw['forwardMap'] = function (this: FieldDecoratorResult, f: () => TYPES): Fiel export const f: FieldDecoratorResult & { type: (type: TYPES) => FieldDecoratorResult, array: (type: TYPES) => FieldDecoratorResult, + enum: (type: any, allowLabelsAsValue?: boolean) => FieldDecoratorResult, + any: () => FieldDecoratorResult, map: (type: TYPES) => FieldDecoratorResult, forward: (f: () => TYPES) => FieldDecoratorResult, forwardArray: (f: () => TYPES) => FieldDecoratorResult, forwardMap: (f: () => TYPES) => FieldDecoratorResult, } = fRaw as any; -/** - * Same as @Field() but defines type as 'any'. With type any no transformation is applied. - * - * ```typescript - * class User { - * @FieldAny() - * config: any; - * - * @FieldAny([]) - * configs: any[]; - * - * @FieldAny({}) - * configMap: {[name: string]: any}; - * } - * - * ``` - * - * @category Decorator - */ -export function FieldAny(type?: {} | Array) { - if (isObject(type)) { - return Field({Any}); - } - - if (isArray(type)) { - return Field([Any]); - } - - return Field(Any); -} - -/** - * Same as @Field(T) but defines the field as array of type T. - * This is the same as @Field({T}). - * - * Use this method if you reference a circular dependency class, e.g. - * ```typescript - * - * class User { - * @FieldMap(() => MyConfig) - * config: {[k: string]: MyConfig}; - * } - * ``` - * - * ```typescript - * class User { - * @FieldMap(String) - * tags: {[k: string]: string}; - * - * @Field({String}) - * tags: {[k: string]: string}; - * } - * ``` - * - * @category Decorator - */ -export function FieldMap(type: FieldTypes) { - return Field({type}); -} - -/** - * Same as @Field(T) but defines the field as map of type T. - * - * ```typescript - * class User { - * @FieldArray(String) - * tags: string[]; - * - * @Field([String]) - * tags: string[]; - * } - * ``` - * - * @category Decorator - */ -export function FieldArray(type: FieldTypes) { - return Field([type]); -} - /** * @hidden */ function Type(type: Types) { - return FieldDecoratorWrapper((target, property, returnType?: any) => { + return FieldDecoratorWrapper((target, property, returnType: any) => { property.type = type; }); } @@ -1274,28 +1187,22 @@ function Type(type: Types) { * } * } * ``` - * - * @category Decorator */ -export function MongoIdField() { +function MongoIdField() { return Type('objectId'); } /** * Used to define a field as UUID (v4). - * - * @category Decorator */ -export function UUIDField() { +function UUIDField() { return Type('uuid'); } /** * Used to define an index on a field. - * - * @category Decorator */ -export function Index(options?: IndexOptions, name?: string) { +function Index(options?: IndexOptions, name?: string) { return FieldDecoratorWrapper((target, property) => { const schema = getOrCreateEntitySchema(target); if (property.methodName) { @@ -1323,10 +1230,8 @@ export function MultiIndex(fields: string[], options: IndexOptions, name?: strin * Used to define a field as Enum. * * If allowLabelsAsValue is set, you can use the enum labels as well for setting the property value using plainToClass(). - * - * @category Decorator */ -export function EnumField(type: any, allowLabelsAsValue = false) { +function EnumField(type: any, allowLabelsAsValue = false) { return FieldDecoratorWrapper((target, property, returnType?: any) => { if (property) { Type('enum')(target, property.name); diff --git a/packages/core/src/validation.ts b/packages/core/src/validation.ts index 7d19c7275..9d9aeff87 100644 --- a/packages/core/src/validation.ts +++ b/packages/core/src/validation.ts @@ -194,17 +194,6 @@ export class RequiredValidator implements PropertyValidator { } } -/** - * Used to mark a field as optional. The validation requires field values per default, this makes it optional. - * - * @category Decorator - */ -export function Optional() { - return FieldDecoratorWrapper((target, property) => { - property.isOptional = true; - }); -} - /** * @hidden */ diff --git a/packages/core/tests/big-entity.ts b/packages/core/tests/big-entity.ts index 72b7eccca..2f888b478 100644 --- a/packages/core/tests/big-entity.ts +++ b/packages/core/tests/big-entity.ts @@ -1,72 +1,67 @@ import { Entity, - EnumField, - Field, - FieldAny, - IDField, - Optional, - UUIDField + f } from "../index"; export class JobConfigDocker { - @Field([String]) + @f.array(String) env: string[] = []; //e.g. ["PATH=bla"] - @Field([String]) + @f.array(String) binds: string[] = []; //e.g. ["/tmp:/tmp"] - @Field([String]) + @f.array(String) links: string[] = []; //e.g. ["redis3:redis"] } export class JobResources { - @Field() + @f cpu: number = 1; - @Field() + @f memory: number = 1; - @Field() + @f gpu: number = 0; - @Field() + @f gpuMemory: number = 0; } export class JobTaskCommand { - constructor(@Field() public name: string, @Field() public command: string) { + constructor(@f public name: string, @f public command: string) { } } export class JobTaskConfigBase { - @Field([String]) + @f.array(String) install?: string[]; - @Field() + @f dockerfile: string = ''; - @Field([String]) + @f.array(String) install_files?: string[]; - @Field() + @f image: string = ''; - @Field([String]) + @f.array(String) environmentVariables: string[] = []; - @Field([String]) + @f.array(String) servers: string[] = []; - @Field([JobTaskCommand]) + @f.array(JobTaskCommand) commands: JobTaskCommand[] = []; - @Field([String]) + @f.array(String) args?: string[]; - @Field(JobResources) + @f resources: JobResources = new JobResources; - @Field(JobConfigDocker) + @f docker: JobConfigDocker = new JobConfigDocker; } @@ -74,13 +69,13 @@ export class JobTaskConfig extends JobTaskConfigBase { /** * Will be set by config loading. */ - @Field() + @f name: string = ''; - @Field() + @f replicas: number = 1; - @Field([String]) + @f.array(String) depends_on: string[] = []; } @@ -98,79 +93,79 @@ export class JobConfig extends JobTaskConfigBase { 'docker' ]; - @FieldAny() + @f.any() parameters: { [name: string]: any } = {}; - @Field([String]) + @f.array(String) ignore: string[] = []; - @Field() + @f priority: number = 0; - @Field() + @f import: string = ''; - @Field({JobTaskConfig}) + @f.map(JobTaskConfig) tasks: { [name: string]: JobTaskConfig } = {}; } export class JobEnvironmentPython { - @Field() + @f version?: string; - @Field() + @f binary?: string; - @Field() + @f sdkVersion?: string; - @Field({String}) + @f.map(String) pipPackages: { [name: string]: string } = {}; } export class JobEnvironment { - @Field() + @f hostname?: string; - @Field() + @f username?: string; - @Field() + @f platform?: string; - @Field() + @f release?: string; - @Field() + @f arch?: string; - @Field() + @f uptime?: number; - @Field() + @f nodeVersion?: string; - @Field({String}) + @f.map(String) environmentVariables?: { [name: string]: string }; - @Field(JobEnvironmentPython) + @f python?: JobEnvironmentPython; } export class JobGit { - @Field() + @f author?: string; - @Field() + @f branch?: string; - @Field() + @f commit?: string; - @Field() + @f message?: string; - @Field() + @f origin?: string; } @@ -179,25 +174,25 @@ export class JobDocker { } export class JobDockerImage { - @Field() + @f name?: string; - @Field() + @f id?: string; - @Field() + @f size?: number; - @Field() + @f os?: string; - @Field() + @f arch?: string; - @Field() + @f created?: Date; - @Field() + @f builtWithDockerVersion?: string; } @@ -247,107 +242,107 @@ export enum JobTaskInstanceStatus { } export class Channel { - @Field([String]) + @f.array(String) traces: string[] = []; - @Field() + @f main?: boolean; - @Field() + @f kpi?: boolean; - @Field() + @f kpiTrace: number = 0; - @Field() + @f maxOptimization: boolean = true; - @FieldAny([]) + @f.any().asArray() lastValue: any[] = []; - @FieldAny() + @f.any() xaxis?: object; - @FieldAny() + @f.any() yaxis?: object; - @FieldAny() + @f.any() layout?: object; } export class JobAssignedResourcesGpu { - constructor(@Field() public id: number, @Field() public memory: number) { + constructor(@f public id: number, @f public memory: number) { } } export class JobAssignedResources { - @Field() + @f cpu: number = 0; - @Field() + @f memory: number = 0; - @Field([JobAssignedResourcesGpu]) + @f.array(JobAssignedResourcesGpu) gpus: JobAssignedResourcesGpu[] = []; } export class JobTaskInstance { - @EnumField(JobTaskInstanceStatus) + @f.enum(JobTaskInstanceStatus) status: JobTaskInstanceStatus = JobTaskInstanceStatus.pending; - @Field(JobEnvironment) + @f.type(JobEnvironment) environment: JobEnvironment = new JobEnvironment; - @UUIDField() + @f.uuid() server?: string; - @Field(JobAssignedResources) + @f.type(JobAssignedResources) assignedResources: JobAssignedResources = new JobAssignedResources; - constructor(@Field() public id: number) { + constructor(@f public id: number) { } } export class JobTaskQueue { - @Field() + @f position: number = 0; - @Field() + @f tries: number = 0; - @Field() + @f result: string = ''; - @Field() + @f added: Date = new Date(); } export class JobTask { - @Field(JobTaskQueue) + @f queue: JobTaskQueue = new JobTaskQueue; - @Field() + @f name: string; - @Field(JobDocker) + @f docker: JobDocker = new JobDocker; - @Field(JobDockerImage) + @f dockerImage: JobDockerImage = new JobDockerImage; - @EnumField(JobTaskStatus) + @f.enum(JobTaskStatus) status: JobTaskStatus = JobTaskStatus.pending; - @Field() + @f assigned?: Date; - @Field() + @f started?: Date; - @Field() + @f ended?: Date; - @Field({JobTaskInstance}) + @f.map(JobTaskInstance) private instances: { [name: string]: JobTaskInstance } = {}; constructor(name: string, replicas: number) { @@ -359,94 +354,92 @@ export class JobTask { @Entity('job', 'jobs') export class Job { - @IDField() - @UUIDField() + @f.id().uuid() id: string; - @UUIDField() + @f.uuid() project: string; - @Field() + @f number: number = 0; - @Field() + @f version: number = 1; - @Field() + @f created: Date = new Date(); - @Field() + @f updated: Date = new Date(); - @Field() + @f author?: string; - @Field(JobConfig) + @f config: JobConfig = new JobConfig; - @Field(JobGit) - @Optional() + @f.optional() git?: JobGit; - @Field() + @f configFile?: string; - @EnumField(JobStatus) + @f.enum(JobStatus) status: JobStatus = JobStatus.creating; - @Field() + @f title: string = ''; - @Field({JobTask}) + @f.map(JobTask) tasks: { [name: string]: JobTask } = {}; - @Field() + @f runOnCluster: boolean = false; - @Field() + @f assigned?: Date; - @Field() + @f started?: Date; - @Field() + @f ended?: Date; - @Field() + @f ping?: Date; - @Field() + @f iteration: number = 0; - @Field() + @f iterations: number = 0; - @Field() + @f secondsPerIteration: number = 0; //aka batches - @Field() + @f step: number = 0; - @Field() + @f steps: number = 0; - @Field() + @f stepLabel: string = 'step'; /** * ETA in seconds. Time left. */ - @Field() + @f eta: number = 0; - @Field() + @f speed: number = 0; - @Field() + @f speedLabel: string = 'sample/s'; - @Field({Channel}) + @f.map(Channel) channels: { [name: string]: Channel } = {}; constructor(id: string, project: string) { diff --git a/packages/core/tests/decorator.spec.ts b/packages/core/tests/decorator.spec.ts index 541a6170f..0984d220e 100644 --- a/packages/core/tests/decorator.spec.ts +++ b/packages/core/tests/decorator.spec.ts @@ -4,21 +4,19 @@ import { classToPlain, DatabaseName, Entity, - Field, + f, getDatabaseName, getEntityName, getClassSchema, getParentReferenceClass, getReflectionType, - IDField, isArrayType, isMapType, - MongoIdField, ParentReference, plainToClass, RegisteredEntities, - f, - FieldAny, FieldMap, forwardRef, FieldArray, isOptional, isRegisteredEntity, + isOptional, + isRegisteredEntity, } from "../"; import {Buffer} from "buffer"; import {SimpleModel} from "./entities"; @@ -29,7 +27,7 @@ import {getClassTypeFromInstance} from '../src/decorators'; test('test invalid usage decorator', async () => { expect(() => { - @Field() + @f class Base { ohwe: any; } @@ -70,13 +68,13 @@ test('test invalid usage', async () => { } class Base { - @Field(forwardRef(() => undefined)) + @f.forward(() => undefined!) ohwe: any; - @Field(forwardRef(() => Config)) + @f.forward(() => Config) config?: any; - constructor(@Field() public id: string) { + constructor(@f public id: string) { } } @@ -112,7 +110,7 @@ test('test entity database', async () => { @Entity('DifferentDataBase', 'differentCollection') @DatabaseName('testing1') class DifferentDataBase { - @f.id().mongo() + @f.id().mongoId() _id?: string; @f @@ -222,117 +220,6 @@ test('test @Field', () => { expect(schema.getProperty('configArray').isMap).toBe(false); }); -test('test invalid @Field', () => { - class Config { - @f.optional() name?: string; - } - - expect(() => { - class User { - @Field() - notDefined; - } - }).toThrowError('User::notDefined type mismatch. Given undefined, but declared is Object or undefined.'); - - expect(() => { - var NOTEXIST; - - class User { - @Field(NOTEXIST) - notDefined; - } - }).toThrowError('User::notDefined type mismatch. Given undefined, but declared is Object or undefined.'); - - expect(() => { - class User { - @Field() - created = new Date; - } - }).toThrowError('User::created type mismatch. Given undefined, but declared is Object or undefined.'); - - expect(() => { - class User { - @Field(Config) - config: Config[] = []; - } - }).toThrowError('User::config type mismatch. Given Config, but declared is Array.'); - - expect(() => { - class User { - @Field([Config]) - config?: Config; - } - }).toThrowError('User::config type mismatch. Given Config[], but declared is Config.'); - - expect(() => { - class User { - @Field(Config) - config: { [k: string]: Config } = {}; - } - }).toThrowError('User::config type mismatch. Given Config, but declared is Object or undefined'); - - expect(() => { - class Model { - @Field([forwardRef(() => Config)]) - sub?: Config; - } - - }).toThrowError('Model::sub type mismatch. Given ForwardedRef[], but declared is Config.'); - - expect(() => { - class Model { - @FieldArray(forwardRef(() => Config)) - sub?: Config; - } - - }).toThrowError('Model::sub type mismatch. Given ForwardedRef[], but declared is Config.'); - - expect(() => { - class Model { - @FieldMap(forwardRef(() => Config)) - sub?: Config; - } - }).toThrowError('Model::sub type mismatch. Given {[key: string]: ForwardedRef}, but declared is Config.'); - - expect(() => { - class Model { - @Field({Config}) - sub?: Config[]; - } - - }).toThrowError('Model::sub type mismatch. Given {[key: string]: Config}, but declared is Array.'); - - expect(() => { - class Model { - @FieldAny() - any?: any[]; - } - }).toThrowError('Model::any type mismatch. Given Any, but declared is Array.'); - - { - //works - class Model { - @FieldAny() - any?: { [k: string]: any }; - } - } - { - //works - class Model { - @FieldAny({}) - any?; - } - } - - { - //works - class Model { - @Field(forwardRef(() => Config)) - sub?: Config; - } - } -}); - test('test no entity throw error', () => { expect(() => { @@ -375,7 +262,7 @@ test('test decorator circular', () => { { class Model { - @Field(forwardRef(() => Sub)) + @f.forward(() => Sub) sub?: Sub; } @@ -384,7 +271,7 @@ test('test decorator circular', () => { { class Model { - @FieldMap(forwardRef(() => Sub)) + @f.forwardMap(() => Sub) sub?: { [l: string]: Sub }; } @@ -394,7 +281,7 @@ test('test decorator circular', () => { { class Model { - @Field([forwardRef(() => Sub)]) + @f.forwardArray(() => Sub) sub?: Sub[]; } @@ -412,20 +299,19 @@ test('test properties', () => { @Entity('Model') class Model { - @IDField() - @MongoIdField() + @f.id().mongoId() _id?: string; - @Field() + @f name?: string; - @Field(DataValue) + @f.type(DataValue) data?: DataValue; } @Entity('SubModel') class SubModel extends Model { - @Field(DataValue2) + @f.type(DataValue2) data2?: DataValue2; } @@ -466,10 +352,10 @@ test('test properties', () => { test('more decorator', () => { class Model { - @Field() + @f bool: boolean = false; - @FieldAny() + @f.any() whatever: any; } @@ -528,13 +414,13 @@ test('more decorator', () => { test('more array/map', () => { class Model { - @Field([Boolean]) + @f.array(Boolean) bools?: boolean[]; - @FieldAny([]) + @f.any().asArray() whatever?: any[]; - @FieldAny({}) + @f.any().asMap() whatevermap?: { [k: string]: any }; } @@ -545,7 +431,7 @@ test('more array/map', () => { test('binary', () => { class Model { - @Field(Buffer) + @f.type(Buffer) preview: Buffer = Buffer.from('FooBar', 'utf8'); } diff --git a/packages/core/tests/decorator2.spec.ts b/packages/core/tests/decorator2.spec.ts index 3d17f61f9..d85b80d14 100644 --- a/packages/core/tests/decorator2.spec.ts +++ b/packages/core/tests/decorator2.spec.ts @@ -92,25 +92,130 @@ test('test inheritance', async () => { class Super3 extends Super2 { } - // expect(getClassSchema(Base).getProperty('id').type).toBe('string'); - // expect(getClassSchema(Base).getIndex('id2')!.name).toBe('id2'); - // - // expect(getClassSchema(Page).getProperty('id').type).toBe('string'); - // expect(getClassSchema(Page).getProperty('name').type).toBe('string'); - // expect(getClassSchema(Page).getIndex('id2')!.name).toBe('id2'); - // - // expect(getClassSchema(SuperPage).getProperty('id').type).toBe('string'); - // expect(getClassSchema(SuperPage).getProperty('name').type).toBe('string'); + expect(getClassSchema(Base).getProperty('id').type).toBe('string'); + expect(getClassSchema(Base).getIndex('id2')!.name).toBe('id2'); + + expect(getClassSchema(Page).getProperty('id').type).toBe('string'); + expect(getClassSchema(Page).getProperty('name').type).toBe('string'); + expect(getClassSchema(Page).getIndex('id2')!.name).toBe('id2'); + + expect(getClassSchema(SuperPage).getProperty('id').type).toBe('string'); + expect(getClassSchema(SuperPage).getProperty('name').type).toBe('string'); expect(getClassSchema(SuperPage).getProperty('super').type).toBe('number'); - // expect(getClassSchema(SuperPage).getIndex('id2')!.name).toBe('id2'); - // - // expect(getClassSchema(Super3).getProperty('id').type).toBe('string'); - // expect(getClassSchema(Super3).getProperty('name').type).toBe('string'); - // expect(getClassSchema(Super3).getProperty('super').type).toBe('number'); - // expect(getClassSchema(Super3).getIndex('id2')!.name).toBe('id2'); - // - // expect(getClassSchema(Super2).getProperty('id').type).toBe('string'); - // expect(getClassSchema(Super2).getProperty('name').type).toBe('string'); - // expect(getClassSchema(Super2).getProperty('super').type).toBe('number'); - // expect(getClassSchema(Super2).getIndex('id2')!.name).toBe('id2'); + expect(getClassSchema(SuperPage).getIndex('id2')!.name).toBe('id2'); + + expect(getClassSchema(Super3).getProperty('id').type).toBe('string'); + expect(getClassSchema(Super3).getProperty('name').type).toBe('string'); + expect(getClassSchema(Super3).getProperty('super').type).toBe('number'); + expect(getClassSchema(Super3).getIndex('id2')!.name).toBe('id2'); + + expect(getClassSchema(Super2).getProperty('id').type).toBe('string'); + expect(getClassSchema(Super2).getProperty('name').type).toBe('string'); + expect(getClassSchema(Super2).getProperty('super').type).toBe('number'); + expect(getClassSchema(Super2).getIndex('id2')!.name).toBe('id2'); +}); + + + +test('test invalid @Field', () => { + class Config { + @f.optional() name?: string; + } + + expect(() => { + class User1 { + @f + notDefined; + } + }).toThrowError('User1::notDefined type mismatch. Given undefined, but declared is Object or undefined.'); + + expect(() => { + var NOTEXIST; + + class User2 { + @f.type(NOTEXIST) + notDefined; + } + }).toThrowError('User2::notDefined type mismatch. Given undefined, but declared is Object or undefined.'); + + expect(() => { + class User3 { + @f + created = new Date; + } + }).toThrowError('User3::created type mismatch. Given undefined, but declared is Object or undefined.'); + + expect(() => { + class User4 { + @f.type(Config) + config: Config[] = []; + } + }).toThrowError('User4::config type mismatch. Given Config, but declared is Array.'); + + expect(() => { + class User5 { + @f.array(Config) + config?: Config; + } + }).toThrowError('User5::config type mismatch. Given Config[], but declared is Config.'); + + expect(() => { + class User6 { + @f.type(Config) + config: { [k: string]: Config } = {}; + } + }).toThrowError('User6::config type mismatch. Given Config, but declared is Object or undefined'); + + expect(() => { + class Model { + @f.forwardArray(() => Config) + sub?: Config; + } + + }).toThrowError('Model::sub type mismatch. Given ForwardedRef[], but declared is Config.'); + + expect(() => { + class Model { + @f.forwardMap(() => Config) + sub?: Config; + } + }).toThrowError('Model::sub type mismatch. Given {[key: string]: ForwardedRef}, but declared is Config.'); + + expect(() => { + class Model { + @f.map(Config) + sub?: Config[]; + } + + }).toThrowError('Model::sub type mismatch. Given {[key: string]: Config}, but declared is Array.'); + + expect(() => { + class Model { + @f.any() + any?: any[]; + } + }).toThrowError('Model::any type mismatch. Given Any, but declared is Array.'); + + { + //works + class Model { + @f.any() + any?: { [k: string]: any }; + } + } + { + //works + class Model { + @f.any().asMap() + any?; + } + } + + { + //works + class Model { + @f.forward(() => Config) + sub?: Config; + } + } }); diff --git a/packages/core/tests/document-scenario/DocumentClass.ts b/packages/core/tests/document-scenario/DocumentClass.ts index 442687dde..796d55c25 100644 --- a/packages/core/tests/document-scenario/DocumentClass.ts +++ b/packages/core/tests/document-scenario/DocumentClass.ts @@ -1,32 +1,30 @@ import {PageCollection} from "./PageCollection"; -import {ParentReference, IDField, MongoIdField, Field} from "../../src/decorators"; +import {ParentReference, f} from "../../src/decorators"; import {PageClass} from "./PageClass"; export class DocumentClass { - @IDField() - @MongoIdField() + @f.id().mongoId() _id?: string; - @Field() + @f name?: string; - @Field(PageCollection) + @f.type(PageCollection) pages: PageCollection = new PageCollection; - @Field(PageClass) + @f.type(PageClass) page?: PageClass; } export class ImpossibleToMetDocumentClass { - @IDField() - @MongoIdField() + @f.id().mongoId() _id?: string; - @Field() + @f name?: string; - @Field(PageCollection) + @f.type(PageCollection) pages: PageCollection = new PageCollection; constructor(pages: PageCollection) { @@ -34,7 +32,7 @@ export class ImpossibleToMetDocumentClass { } export class ClassWithUnmetParent { - @Field(ClassWithUnmetParent) + @f.type(ClassWithUnmetParent) @ParentReference() parent?: ClassWithUnmetParent; } diff --git a/packages/core/tests/document-scenario/PageClass.ts b/packages/core/tests/document-scenario/PageClass.ts index 02c2e6c1e..468dc7d31 100644 --- a/packages/core/tests/document-scenario/PageClass.ts +++ b/packages/core/tests/document-scenario/PageClass.ts @@ -1,30 +1,30 @@ import {PageCollection} from "./PageCollection"; import {DocumentClass} from "./DocumentClass"; -import {Entity, Field, forwardRef, ParentReference, UUIDField} from "../../src/decorators"; +import {Entity, f, ParentReference} from "../../src/decorators"; import {uuid} from "../../src/utils"; import {Buffer} from 'buffer'; @Entity('PageClass') export class PageClass { - @UUIDField() + @f.uuid() id: string = uuid(); - @Field(forwardRef(() => PageCollection)) + @f.forward(() => PageCollection) children: PageCollection = new PageCollection; - @Field(Buffer) + @f.type(Buffer) picture?: Buffer; - @Field().optional() + @f.forward(() => PageClass).optional() @ParentReference() parent?: PageClass; constructor( - @Field(forwardRef(() => DocumentClass)) + @f.forward(() => DocumentClass) @ParentReference() public readonly document: DocumentClass, - @Field() + @f public readonly name: string ) { this.document = document; diff --git a/packages/core/tests/document-scenario/PageCollection.ts b/packages/core/tests/document-scenario/PageCollection.ts index acb8d4d7a..cbea3985f 100644 --- a/packages/core/tests/document-scenario/PageCollection.ts +++ b/packages/core/tests/document-scenario/PageCollection.ts @@ -1,9 +1,8 @@ -import {Field, Decorated, forwardRef, f} from "../../src/decorators"; +import {forwardRef, f} from "../../src/decorators"; import {PageClass} from "./PageClass"; export class PageCollection { - @f.array(forwardRef(() => PageClass)) - @Decorated() + @f.array(forwardRef(() => PageClass)).decorated() private readonly pages: PageClass[] = []; constructor(pages: PageClass[] = []) { diff --git a/packages/core/tests/entities.ts b/packages/core/tests/entities.ts index 35c172386..c739e3541 100644 --- a/packages/core/tests/entities.ts +++ b/packages/core/tests/entities.ts @@ -1,44 +1,34 @@ import { - Decorated, Entity, - EnumField, - Field, - FieldAny, - IDField, - Index, - MongoIdField, - Optional, + f, uuid, - UUIDField, MultiIndex } from '../index'; export class JobTaskQueue { - @Field() + @f position: number = 0; - @Field() + @f tries: number = 0; - @Field() + @f result: string = ''; - @Field() + @f added: Date = new Date(); } @Entity('sub') export class SubModel { - @Field() + @f label: string; - @Field() - @Optional() + @f.optional() age?: number; - @Field(JobTaskQueue) - @Optional() + @f.type(JobTaskQueue).optional() queue?: JobTaskQueue; constructorUsed = false; @@ -58,8 +48,7 @@ export enum Plan { export const now = new Date(); export class CollectionWrapper { - @Field([SubModel]) - @Decorated() + @f.array(SubModel).decorated() public items: SubModel[]; constructor(items: SubModel[]) { @@ -72,8 +61,7 @@ export class CollectionWrapper { } export class StringCollectionWrapper { - @Decorated() - @Field([String]) + @f.array(String).decorated() public items: string[]; constructor(items: string[]) { @@ -88,61 +76,57 @@ export class StringCollectionWrapper { @Entity('SimpleModel') @MultiIndex(['name', 'type'], {unique: true}) export class SimpleModel { - @IDField() - @UUIDField() + @f.id().uuid() id: string = uuid(); - @Field() - @Index() + @f.index() name: string; - @Field() + @f type: number = 0; - @Field() + @f yesNo: boolean = false; - @EnumField(Plan) + @f.enum(Plan) plan: Plan = Plan.DEFAULT; - @Field() + @f created: Date = now; - @Field([String]) + @f.array(String) types: string[] = []; - @Field() - @Optional() + @f.optional() child?: SubModel; - @Field() - @Optional() + @f.optional() selfChild?: SimpleModel; - @Field([SubModel]) + @f.array(SubModel) children: SubModel[] = []; - @Field({SubModel}) + @f.map(SubModel) childrenMap: { [key: string]: SubModel } = {}; - @Field(CollectionWrapper) + @f.type(CollectionWrapper) childrenCollection: CollectionWrapper = new CollectionWrapper([]); - @Field(StringCollectionWrapper) + @f.type(StringCollectionWrapper) stringChildrenCollection: StringCollectionWrapper = new StringCollectionWrapper([]); notMapped: { [key: string]: any } = {}; - @FieldAny() + @f.any() anyField: any; - @Field().exclude() + @f.exclude() excluded: string = 'default'; - @Field().exclude('mongo') + @f.exclude('mongo') excludedForMongo: string = 'excludedForMongo'; - @Field().exclude('plain') + @f.exclude('plain') excludedForPlain: string = 'excludedForPlain'; constructor(name: string) { @@ -152,24 +136,22 @@ export class SimpleModel { @Entity('SuperSimple') export class SuperSimple { - @IDField() - @MongoIdField() + @f.id().mongoId() _id?: string; - @Field() + @f name?: string; } @Entity('BaseClass') export class BaseClass { - @IDField() - @MongoIdField() + @f.id().mongoId() _id?: string; } @Entity('ChildClass') export class ChildClass extends BaseClass { - @Field() + @f name?: string; } diff --git a/packages/core/tests/method-decorators.spec.ts b/packages/core/tests/method-decorators.spec.ts index 5a67fce06..f7ed693b5 100644 --- a/packages/core/tests/method-decorators.spec.ts +++ b/packages/core/tests/method-decorators.spec.ts @@ -1,15 +1,14 @@ import 'jest'; import 'jest-extended'; import 'reflect-metadata'; -import {Decorated, f, Field, getClassSchema} from "../src/decorators"; +import {f, getClassSchema} from "../src/decorators"; test('Basic array', () => { class Other { } class Controller { - @Field([Other]) - @Decorated() + @f.array(Other).decorated() protected readonly bar: Other[] = []; } @@ -71,10 +70,10 @@ test('short @f no index on arg', () => { test('method args', () => { class Controller { - public foo(@Field() bar: string) { + public foo(@f bar: string) { } - public foo2(@Field() bar: string, optional?: true, @Field().optional() anotherOne?: boolean) { + public foo2(@f bar: string, optional?: true, @f.optional() anotherOne?: boolean) { } } @@ -121,6 +120,52 @@ test('short @f', () => { }); +test('short @f multi', () => { + class Controller { + public foo(@f bar: string, @f foo: number) { + } + } + + const s = getClassSchema(Controller); + { + const props = s.getMethodProperties('foo'); + + expect(props).toBeArrayOfSize(2); + expect(props[0].name).toBe('0'); + expect(props[0].type).toBe('string'); + expect(props[0].isArray).toBe(false); + + expect(props[1].name).toBe('1'); + expect(props[1].type).toBe('number'); + expect(props[1].isArray).toBe(false); + } +}); + + +test('short @f multi gap', () => { + class Controller { + public foo(@f bar: string, nothing: boolean, @f foo: number) { + } + } + + const s = getClassSchema(Controller); + { + const props = s.getMethodProperties('foo'); + + expect(props).toBeArrayOfSize(3); + expect(props[0].name).toBe('0'); + expect(props[0].type).toBe('string'); + expect(props[0].isArray).toBe(false); + + expect(props[1]).toBeUndefined(); + + expect(props[2].name).toBe('2'); + expect(props[2].type).toBe('number'); + expect(props[2].isArray).toBe(false); + } +}); + + test('short @f with type', () => { class Controller { public foo(@f.array(String) bar: string[]) { diff --git a/packages/core/tests/plain-to.spec.ts b/packages/core/tests/plain-to.spec.ts index 7d63efc4c..38595fa96 100644 --- a/packages/core/tests/plain-to.spec.ts +++ b/packages/core/tests/plain-to.spec.ts @@ -9,7 +9,7 @@ import { partialPlainToClass, plainToClass } from "../src/mapper"; -import {EnumField, Field, forwardRef, Optional, ParentReference} from ".."; +import {f, ParentReference} from ".."; import {DocumentClass} from "./document-scenario/DocumentClass"; import {PageClass} from "./document-scenario/PageClass"; import {PageCollection} from "./document-scenario/PageCollection"; @@ -278,7 +278,7 @@ test('test enum string', () => { } class Model { - @EnumField(MyEnum) + @f.enum(MyEnum) enum: MyEnum = MyEnum.waiting; } @@ -298,7 +298,7 @@ test('test enum labels', () => { } class Model { - @EnumField(MyEnum) + @f.enum(MyEnum) enum: MyEnum = MyEnum.third; } @@ -314,7 +314,7 @@ test('test enum labels', () => { class ModelWithLabels { - @EnumField(MyEnum, true) + @f.enum(MyEnum, true) enum: MyEnum = MyEnum.third; } @@ -339,18 +339,17 @@ test('partial edge cases', () => { class Config {} class User { - @Field() + @f name?: string; - @Field([String]) + @f.array(String) tags?: string[]; - @Field() + @f config?: Config; - @Field(forwardRef(() => User)) + @f.forward(() => User).optional() @ParentReference() - @Optional() parent?: User; } diff --git a/packages/core/tests/validation.spec.ts b/packages/core/tests/validation.spec.ts index 2a011bcb0..5e2614a5d 100644 --- a/packages/core/tests/validation.spec.ts +++ b/packages/core/tests/validation.spec.ts @@ -2,10 +2,7 @@ import 'reflect-metadata'; import 'jest-extended' import { AddValidator, - Field, InlineValidator, - MongoIdField, - Optional, plainToClass, PropertyValidator, PropertyValidatorError, @@ -15,13 +12,13 @@ import { ValidationFailed } from "../"; import {CustomError, isPlainObject} from '@marcj/estdlib'; -import {Decorated, FieldAny, FieldArray, getClassSchema, UUIDField} from "../src/decorators"; +import {getClassSchema, f} from "../src/decorators"; test('test simple', async () => { class Page { constructor( - @Field() public name: string, - @Field() public age: number, + @f public name: string, + @f public age: number, ) { } } @@ -36,27 +33,29 @@ test('test simple', async () => { test('test required', async () => { class Model { - @Field() + @f id: string = '1'; - @Field() + @f name?: string; - @Optional() + @f.optional() optional?: string; - @Optional() - @Field({String}) + @f.map(String).optional() map?: { [name: string]: string }; - @Optional() - @Field([String]) + @f.array(String).optional() array?: string[]; } const instance = new Model; expect(validate(Model, instance)).toBeArrayOfSize(1); - expect(validate(Model, instance)).toEqual([{code: 'required', message: "Required value is undefined", path: 'name'}]); + expect(validate(Model, instance)).toEqual([{ + code: 'required', + message: "Required value is undefined", + path: 'name' + }]); expect(validate(Model, { name: 'foo', @@ -74,30 +73,38 @@ test('test required', async () => { test('test deep', async () => { class Deep { - @Field() + @f name?: string; } class Model { - @Field() + @f id: string = '2'; - @Field(Deep) + @f.type(Deep) deep?: Deep; - @Field([Deep]) + @f.array(Deep) deepArray: Deep[] = []; - @Field({Deep}) + @f.map(Deep) deepMap: { [name: string]: Deep } = {}; } const instance = new Model; expect(validate(Model, instance)).toBeArrayOfSize(1); - expect(validate(Model, instance)).toEqual([{code: 'required', message: "Required value is undefined", path: 'deep'}]); + expect(validate(Model, instance)).toEqual([{ + code: 'required', + message: "Required value is undefined", + path: 'deep' + }]); instance.deep = new Deep(); - expect(validate(Model, instance)).toEqual([{code: 'required', message: "Required value is undefined", path: 'deep.name'}]); + expect(validate(Model, instance)).toEqual([{ + code: 'required', + message: "Required value is undefined", + path: 'deep.name' + }]); instance.deep.name = 'defined'; instance.deepArray.push(new Deep()); @@ -129,7 +136,7 @@ test('test AddValidator', async () => { } class Model { - @Field() + @f @AddValidator(MyValidator) id: string = '2'; } @@ -146,7 +153,7 @@ test('test inline validator throw Error', async () => { } class Model { - @Field() + @f @InlineValidator((value: string) => { if (value.length > 5) { throw new MyError(); @@ -161,7 +168,7 @@ test('test inline validator throw Error', async () => { test('test inline validator throw string', async () => { class Model { - @Field() + @f @InlineValidator((value: string) => { if (value.length > 5) { throw 'Too long'; @@ -176,7 +183,7 @@ test('test inline validator throw string', async () => { test('test inline validator', async () => { class Model { - @Field() + @f @InlineValidator((value: string) => { if (value.length > 5) { return new PropertyValidatorError('too_long', 'Too long'); @@ -191,37 +198,73 @@ test('test inline validator', async () => { test('test uuid', async () => { class Model { - @UUIDField() + @f.uuid() public id!: string; } expect(validate(Model, {id: "4cac8ff9-4450-42c9-b736-e4d56f7a832d"})).toEqual([]); - expect(validate(Model, {id: "4cac8ff9-4450-42c9-b736"})).toEqual([{code: 'invalid_uuid', message: 'No UUID given', path: 'id'}]); + expect(validate(Model, {id: "4cac8ff9-4450-42c9-b736"})).toEqual([{ + code: 'invalid_uuid', + message: 'No UUID given', + path: 'id' + }]); expect(validate(Model, {id: "xxxx"})).toEqual([{code: 'invalid_uuid', message: 'No UUID given', path: 'id'}]); expect(validate(Model, {id: ""})).toEqual([{code: 'invalid_uuid', message: 'No UUID given', path: 'id'}]); expect(validate(Model, {id: false})).toEqual([{code: 'invalid_uuid', message: 'No UUID given', path: 'id'}]); - expect(validate(Model, {id: null})).toEqual([{code: 'required', message: 'Required value is undefined', path: 'id'}]); - expect(validate(Model, {id: undefined})).toEqual([{code: 'required', message: 'Required value is undefined', path: 'id'}]); + expect(validate(Model, {id: null})).toEqual([{ + code: 'required', + message: 'Required value is undefined', + path: 'id' + }]); + expect(validate(Model, {id: undefined})).toEqual([{ + code: 'required', + message: 'Required value is undefined', + path: 'id' + }]); }); test('test objectId', async () => { class Model { - @MongoIdField() + @f.mongoId() public id!: string; } expect(validate(Model, {id: "507f191e810c19729de860ea"})).toEqual([]); - expect(validate(Model, {id: "507f191e810c19729de860"})).toEqual([{code: 'invalid_objectid', message: 'No Mongo ObjectID given', path: 'id'}]); - expect(validate(Model, {id: "xxxx"})).toEqual([{code: 'invalid_objectid', message: 'No Mongo ObjectID given', path: 'id'}]); - expect(validate(Model, {id: ""})).toEqual([{code: 'invalid_objectid', message: 'No Mongo ObjectID given', path: 'id'}]); - expect(validate(Model, {id: false})).toEqual([{code: 'invalid_objectid', message: 'No Mongo ObjectID given', path: 'id'}]); - expect(validate(Model, {id: null})).toEqual([{code: 'required', message: 'Required value is undefined', path: 'id'}]); - expect(validate(Model, {id: undefined})).toEqual([{code: 'required', message: 'Required value is undefined', path: 'id'}]); + expect(validate(Model, {id: "507f191e810c19729de860"})).toEqual([{ + code: 'invalid_objectid', + message: 'No Mongo ObjectID given', + path: 'id' + }]); + expect(validate(Model, {id: "xxxx"})).toEqual([{ + code: 'invalid_objectid', + message: 'No Mongo ObjectID given', + path: 'id' + }]); + expect(validate(Model, {id: ""})).toEqual([{ + code: 'invalid_objectid', + message: 'No Mongo ObjectID given', + path: 'id' + }]); + expect(validate(Model, {id: false})).toEqual([{ + code: 'invalid_objectid', + message: 'No Mongo ObjectID given', + path: 'id' + }]); + expect(validate(Model, {id: null})).toEqual([{ + code: 'required', + message: 'Required value is undefined', + path: 'id' + }]); + expect(validate(Model, {id: undefined})).toEqual([{ + code: 'required', + message: 'Required value is undefined', + path: 'id' + }]); }); test('test boolean', async () => { class Model { - @Field() + @f public bo!: boolean; } @@ -248,7 +291,11 @@ test('test boolean', async () => { expect(validate(Model, {bo: '2'})).toEqual([{code: 'invalid_boolean', message: 'No Boolean given', path: 'bo'}]); expect(validate(Model, {bo: 2})).toEqual([{code: 'invalid_boolean', message: 'No Boolean given', path: 'bo'}]); - expect(validate(Model, {bo: 'asdasd'})).toEqual([{code: 'invalid_boolean', message: 'No Boolean given', path: 'bo'}]); + expect(validate(Model, {bo: 'asdasd'})).toEqual([{ + code: 'invalid_boolean', + message: 'No Boolean given', + path: 'bo' + }]); expect(validate(Model, {bo: 233})).toEqual([{code: 'invalid_boolean', message: 'No Boolean given', path: 'bo'}]); expect(validate(Model, {bo: {}})).toEqual([{code: 'invalid_boolean', message: 'No Boolean given', path: 'bo'}]); expect(validate(Model, {bo: []})).toEqual([{code: 'invalid_boolean', message: 'No Boolean given', path: 'bo'}]); @@ -256,7 +303,7 @@ test('test boolean', async () => { test('test Date', async () => { class Model { - @Field(Date) + @f.type(Date) public endTime!: Date; } @@ -267,11 +314,31 @@ test('test Date', async () => { expect(validate(Model, {endTime: "Tue Mar 19 2019 11:39:10 GMT+0100 (Central European Standard Time)"})).toEqual([]); expect(validate(Model, {endTime: date.toString()})).toEqual([]); expect(validate(Model, {endTime: new Date()})).toEqual([]); - expect(validate(Model, {endTime: ''})).toEqual([{code: 'invalid_date', message: 'No Date string given', path: 'endTime'}]); - expect(validate(Model, {endTime: new Date('asdf')})).toEqual([{code: 'invalid_date', message: 'No valid Date given', path: 'endTime'}]); - expect(validate(Model, {endTime: 'asdf'})).toEqual([{code: 'invalid_date', message: 'No valid Date string given', path: 'endTime'}]); - expect(validate(Model, {endTime: null})).toEqual([{code: 'required', message: 'Required value is undefined', path: 'endTime'}]); - expect(validate(Model, {endTime: undefined})).toEqual([{code: 'required', message: 'Required value is undefined', path: 'endTime'}]); + expect(validate(Model, {endTime: ''})).toEqual([{ + code: 'invalid_date', + message: 'No Date string given', + path: 'endTime' + }]); + expect(validate(Model, {endTime: new Date('asdf')})).toEqual([{ + code: 'invalid_date', + message: 'No valid Date given', + path: 'endTime' + }]); + expect(validate(Model, {endTime: 'asdf'})).toEqual([{ + code: 'invalid_date', + message: 'No valid Date string given', + path: 'endTime' + }]); + expect(validate(Model, {endTime: null})).toEqual([{ + code: 'required', + message: 'Required value is undefined', + path: 'endTime' + }]); + expect(validate(Model, {endTime: undefined})).toEqual([{ + code: 'required', + message: 'Required value is undefined', + path: 'endTime' + }]); { const o = plainToClass(Model, {endTime: date.toString()}); @@ -334,7 +401,7 @@ test('test Date', async () => { test('test string', async () => { class Model { - @Field() + @f id: string = '2'; } @@ -345,13 +412,16 @@ test('test string', async () => { expect(validate(Model, {})).toEqual([]); //because defaults are applied class ModelOptional { - @Field() - @Optional() + @f.optional() id?: string; } expect(validate(ModelOptional, {id: '2'})).toEqual([]); - expect(validate(ModelOptional, {id: 2})).toEqual([{code: 'invalid_string', message: "No String given", path: 'id'}]); + expect(validate(ModelOptional, {id: 2})).toEqual([{ + code: 'invalid_string', + message: "No String given", + path: 'id' + }]); expect(validate(ModelOptional, {id: null})).toEqual([]); expect(validate(ModelOptional, {id: undefined})).toEqual([]); expect(validate(ModelOptional, {})).toEqual([]); @@ -359,7 +429,7 @@ test('test string', async () => { test('test number', async () => { class Model { - @Field() + @f id: number = 2; } @@ -371,14 +441,17 @@ test('test number', async () => { expect(validate(Model, {})).toEqual([]); //because defaults are applied class ModelOptional { - @Field() - @Optional() + @f.optional() id?: number; } expect(validate(ModelOptional, {id: 3})).toEqual([]); expect(validate(ModelOptional, {id: '3'})).toEqual([]); - expect(validate(ModelOptional, {id: 'a'})).toEqual([{code: 'invalid_number', message: "No Number given", path: 'id'}]); + expect(validate(ModelOptional, {id: 'a'})).toEqual([{ + code: 'invalid_number', + message: "No Number given", + path: 'id' + }]); expect(validate(ModelOptional, {id: null})).toEqual([]); expect(validate(ModelOptional, {id: undefined})).toEqual([]); expect(validate(ModelOptional, {})).toEqual([]); @@ -386,31 +459,39 @@ test('test number', async () => { test('test array', async () => { class AClass { - @Field([String]) + @f.array(String) public tags: string[] = []; } expect(validate(AClass, {tags: ['Hi']})).toEqual([]); - expect(validate(AClass, {tags: ['hi', 2]})).toEqual([{code: 'invalid_string', message: "No String given", path: 'tags.1'}]); + expect(validate(AClass, {tags: ['hi', 2]})).toEqual([{ + code: 'invalid_string', + message: "No String given", + path: 'tags.1' + }]); }); test('test map', async () => { class AClass { - @Field({String}) - public tags: {[k: string]: string} = {}; + @f.map(String) + public tags: { [k: string]: string } = {}; } expect(validate(AClass, {tags: {'nix': 'yes'}})).toEqual([]); - expect(validate(AClass, {tags: {'nix': 2}})).toEqual([{code: 'invalid_string', message: "No String given", path: 'tags.nix'}]); + expect(validate(AClass, {tags: {'nix': 2}})).toEqual([{ + code: 'invalid_string', + message: "No String given", + path: 'tags.nix' + }]); }); test('test decorated', async () => { class JobInfo { - @Field() + @f name: string; - @FieldAny() + @f.any() value: any; constructor(name: string, value: any) { @@ -420,11 +501,10 @@ test('test decorated', async () => { } class JobInfos { - @Decorated() - @FieldArray(JobInfo) + @f.array(JobInfo).decorated() public items: JobInfo[] = []; - @Field() + @f public thisFieldIsIgnored: string = ''; protected map: { [name: string]: JobInfo } = {}; @@ -456,7 +536,7 @@ test('test decorated', async () => { } class AClass { - @Field(JobInfos) + @f.type(JobInfos) infos: JobInfos = new JobInfos(); } @@ -500,21 +580,21 @@ test('test decorated', async () => { test('test nested validation', async () => { // Class definition with validation rules class A { - @Field() + @f public x!: string; } class B { - @Field() + @f public type!: string; - @Field(A) + @f.type(A) public nested!: A; - @Field({A}) + @f.map(A) public nestedMap!: { [name: string]: A }; - @Field([A]) + @f.array(A) public nesteds!: A[]; } @@ -538,11 +618,10 @@ test('test nested validation', async () => { ]); class BOptional { - @Field() + @f public type!: string; - @Field(A) - @Optional() + @f.type(A).optional() public nested!: A; } @@ -562,80 +641,82 @@ test('test nested validation', async () => { test('test valdiation on real life case', () => { class NodeResourceReservation { - @Field() + @f reserved: number = 0; - @Field() + @f total: number = 0; } class JobAssignedResourcesGpu { constructor( - @Field().asName('uuid') public uuid: string, - @Field().asName('name') public name: string, - @Field().asName('memory') public memory: number, + @f.asName('uuid') public uuid: string, + @f.asName('name') public name: string, + @f.asName('memory') public memory: number, ) { } } class JobAssignedResources { - @Field() + @f cpu: number = 0; - @Field() + @f memory: number = 0; - @FieldArray(JobAssignedResourcesGpu) + @f.array(JobAssignedResourcesGpu) gpus: JobAssignedResourcesGpu[] = []; } class AssignedJobTaskInstance { constructor( - @Field().asName('jobId') public jobId: string, - @Field().asName('jobAccessToken') public jobAccessToken: string, - @Field().asName('taskName') public taskName: string, - @Field().asName('instance') public instance: number, - @Field().asName('assignedResources') public assignedResources: JobAssignedResources, - ) {} + @f.asName('jobId') public jobId: string, + @f.asName('jobAccessToken') public jobAccessToken: string, + @f.asName('taskName') public taskName: string, + @f.asName('instance') public instance: number, + @f.asName('assignedResources') public assignedResources: JobAssignedResources, + ) { + } } class NodeGpuResource { - @Field() + @f reserved: boolean = false; /** * Value in GB. */ - @Field() + @f memory: number = 1; constructor( - @Field().asName('uuid') public uuid: string, - @Field().asName('name') public name: string, + @f.asName('uuid') public uuid: string, + @f.asName('name') public name: string, ) { } } + class NodeResources { - @Field() + @f cpu: NodeResourceReservation = new NodeResourceReservation; - @Field() + @f memory: NodeResourceReservation = new NodeResourceReservation; - @FieldArray(NodeGpuResource) + @f.array(NodeGpuResource) gpu: NodeGpuResource[] = []; - @Field(AssignedJobTaskInstance).asMap() + @f.map(AssignedJobTaskInstance) assignedJobTaskInstances: { [taskInstanceId: string]: AssignedJobTaskInstance } = {}; } const object = { - cpu: { reserved: 4, total: 6 }, - memory: { reserved: 4, total: 4 }, + cpu: {reserved: 4, total: 6}, + memory: {reserved: 4, total: 4}, gpu: [], assignedJobTaskInstances: { 'a09be358-d6ce-477f-829a-0dc27219de34.0.main': { - assignedResources: { cpu: 4, memory: 4, gpus: [] }, + assignedResources: {cpu: 4, memory: 4, gpus: []}, instance: 0, taskName: 'main', jobAccessToken: 'af82a082-493f-4b1b-ad25-1948ccbe32cb', diff --git a/packages/core/tests/validation2.spec.ts b/packages/core/tests/validation2.spec.ts index f80f25e64..cdf76da7c 100644 --- a/packages/core/tests/validation2.spec.ts +++ b/packages/core/tests/validation2.spec.ts @@ -1,46 +1,30 @@ import 'reflect-metadata'; import 'jest-extended' -import {Field, getClassSchema, IDField, UUIDField} from "../src/decorators"; +import {getClassSchema, f} from "../src/decorators"; test('test minimized code', async () => { expect(() => { class ClusterNodeCredentials { - @Field() + @f sshPort: number = 22; constructor( - @UUIDField().asName('nodeId') - @IDField() + @f.id().uuid().asName('nodeId') + @f.id() public e: string ) { } } }).toThrow('Defining multiple Marshal decorators with different names') - { - //this case works since decorators are handled from bottom to up - //thus the asName() decorated the name already for that #0 arg. - class ClusterNodeCredentials { - @Field() - sshPort: number = 22; - - constructor( - @IDField() - @UUIDField().asName('nodeId') - public e: string - ) { - } - } - } - expect(() => { class ClusterNodeCredentials { - @Field() + @f sshPort: number = 22; constructor( - @UUIDField().asName('nodeId') - @IDField().asName('asd') + @f.id().uuid().asName('nodeId') + @f.id().uuid().asName('asd') public e: string ) { } @@ -48,12 +32,12 @@ test('test minimized code', async () => { }).toThrow('Defining multiple Marshal decorators with different names') class ClusterNodeCredentials { - @Field() + @f sshPort: number = 22; constructor( - @UUIDField().asName('nodeId') - @IDField().asName('nodeId') + @f.id().uuid().asName('nodeId') + @f.id().uuid().asName('nodeId') public e: string ) { } diff --git a/packages/mongo/tests/mongo-query.spec.ts b/packages/mongo/tests/mongo-query.spec.ts index 2f5c9aaf9..81aadae8d 100644 --- a/packages/mongo/tests/mongo-query.spec.ts +++ b/packages/mongo/tests/mongo-query.spec.ts @@ -1,10 +1,9 @@ import 'jest'; import {convertClassQueryToMongo, convertPlainQueryToMongo, propertyClassToMongo} from ".."; -import {Field, Decorated} from "@marcj/marshal/src/decorators"; +import {f} from "@marcj/marshal/src/decorators"; class SimpleConfig { - @Decorated() - @Field([String]) + @f.array(String).decorated() items: string[] = []; constructor(items: string[] = []) { @@ -13,16 +12,16 @@ class SimpleConfig { } class Simple { - @Field() + @f public id!: number; - @Field() + @f public price!: number; - @Field() + @f public label!: string; - @Field(SimpleConfig) + @f.type(SimpleConfig) public config: SimpleConfig = new SimpleConfig; } diff --git a/packages/mongo/tests/mongo.spec.ts b/packages/mongo/tests/mongo.spec.ts index 2302f348a..aac841215 100644 --- a/packages/mongo/tests/mongo.spec.ts +++ b/packages/mongo/tests/mongo.spec.ts @@ -3,11 +3,9 @@ import 'reflect-metadata'; import { DatabaseName, Entity, - Field, + f, getDatabaseName, getEntityName, - IDField, - MongoIdField, plainToClass, } from "@marcj/marshal"; import {Binary, ObjectID} from "mongodb"; @@ -42,7 +40,7 @@ test('test save undefined values', async () => { @Entity('undefined-model-value') class Model { constructor( - @Field().optional() + @f.optional() public name?: string){} } const collection = database.getCollection(Model); @@ -224,11 +222,10 @@ test('test databaseName', async () => { @Entity('DifferentDataBase', 'differentCollection') @DatabaseName('testing2') class DifferentDataBase { - @IDField() - @MongoIdField() + @f.id().mongoId() _id?: string; - @Field() + @f name?: string; } @@ -256,10 +253,10 @@ test('no id', async () => { @Entity('NoId') class NoId { - @MongoIdField() + @f.mongoId() _id?: string; - @Field() + @f name?: string; } @@ -287,17 +284,16 @@ test('second object id', async () => { @Entity('SecondObjectId') class SecondObjectId { - @IDField() - @MongoIdField() + @f.id().mongoId() _id?: string; - @Field() + @f name?: string; - @Field(Buffer) + @f.type(Buffer) preview: Buffer = Buffer.from('FooBar', 'utf8'); - @MongoIdField() + @f.mongoId() secondId?: string; } diff --git a/packages/mongo/tests/to-class.spec.ts b/packages/mongo/tests/to-class.spec.ts index 29dde2a09..c5f48e003 100644 --- a/packages/mongo/tests/to-class.spec.ts +++ b/packages/mongo/tests/to-class.spec.ts @@ -3,8 +3,7 @@ import 'reflect-metadata'; import { classToPlain, cloneClass, - EnumField, - Exclude, + f, getEntityName, getIdField, getIdFieldValue, @@ -14,8 +13,7 @@ import { getReflectionType, getParentReferenceClass, ParentReference, - Optional, - OnLoad, Field, forwardRef, FieldAny + OnLoad } from "@marcj/marshal"; import { now, @@ -294,8 +292,7 @@ test('test childrenMap', async () => { test('test allowNull', async () => { class Model { - @Field() - @Optional() + @f.optional() name?: string; } @@ -310,14 +307,14 @@ test('test OnLoad', async () => { let ModelRef; class Sub { - @Field() + @f name?: string; onLoadCallback: (item: Sub) => void; onFullLoadCallback: (item: Sub) => void; - @Field(forwardRef(() => ModelRef)) + @f.forward(() => ModelRef) @ParentReference() parent?: any; @@ -339,14 +336,13 @@ test('test OnLoad', async () => { } class Model { - @Field() - @Optional() + @f.optional() name?: string; - @Field(Sub) + @f.type(Sub) sub?: Sub; - @Field(Sub) + @f.type(Sub) sub2?: Sub; } @@ -401,7 +397,7 @@ test('test setter/getter', async () => { return true; } - @Field([Font]) + @f.array(Font) get fonts(): Font[] { return this._fonts; } @@ -617,13 +613,13 @@ test('test @Decorated with parent', async () => { test('simple string + number + boolean', () => { class Model { - @Field() + @f name?: string; - @Field() + @f age?: number; - @Field() + @f yesNo?: boolean; } @@ -655,23 +651,23 @@ test('simple string + number + boolean', () => { test('cloneClass', () => { class SubModel { - @Field() + @f name?: string; } class DataStruct { - @Field() + @f name?: string; } class Model { - @FieldAny() + @f.any() data: any; - @Field(DataStruct) + @f.type(DataStruct) dataStruct?: DataStruct; - @Field([SubModel]) + @f.array(SubModel) subs?: SubModel[]; } @@ -729,19 +725,19 @@ test('enums', () => { } class Model { - @EnumField(Enum1) + @f.enum(Enum1) enum1?: Enum1; - @EnumField(Enum2) + @f.enum(Enum2) enum2?: Enum2; - @EnumField(Enum3) + @f.enum(Enum3) enum3?: Enum3; - @EnumField(Enum4) + @f.enum(Enum4) enum4?: Enum4; - @EnumField(Enum4, true) + @f.enum(Enum4, true) enumLabels?: Enum4; } diff --git a/packages/mongo/tests/to-mongo.spec.ts b/packages/mongo/tests/to-mongo.spec.ts index 73c3e88d5..34b804f2a 100644 --- a/packages/mongo/tests/to-mongo.spec.ts +++ b/packages/mongo/tests/to-mongo.spec.ts @@ -1,13 +1,10 @@ import 'jest-extended' import 'reflect-metadata'; import { - EnumField, - Field, + f, forwardRef, getResolvedReflection, - MongoIdField, Optional, ParentReference, - UUIDField } from "@marcj/marshal"; import {Plan, SimpleModel, SubModel} from "@marcj/marshal/tests/entities"; import {Binary, ObjectID} from "mongodb"; @@ -67,7 +64,7 @@ test('make sure undefined is undefined', () => { class Model { constructor( - @Field().optional() + @f.optional() public name?: string){} } @@ -109,13 +106,13 @@ test('convert IDs and invalid values', () => { } class Model { - @MongoIdField() + @f.mongoId() id2?: string; - @UUIDField() + @f.uuid() uuid?: string; - @EnumField(Enum) + @f.enum(Enum) enum?: Enum; } @@ -142,7 +139,7 @@ test('convert IDs and invalid values', () => { test('binary', () => { class Model { - @Field(Buffer) + @f.type(Buffer) preview: Buffer = Buffer.from('FooBar', 'utf8'); } @@ -157,7 +154,7 @@ test('binary', () => { test('binary from mongo', () => { class Model { - @Field(Buffer) + @f.type(Buffer) preview: Buffer = Buffer.from('FooBar', 'utf8'); } @@ -382,18 +379,17 @@ test('partial any copy ', () => { test('partial mongo to plain ', () => { class User { - @Field() + @f name?: string; - @Field([String]) + @f.array(String) tags?: string[]; - @Field(Buffer) + @f.type(Buffer) picture?: Buffer; - @Field(forwardRef(() => User)) + @f.forward(() => User).optional() @ParentReference() - @Optional() parent?: User; } diff --git a/packages/mongo/tests/to-plain.spec.ts b/packages/mongo/tests/to-plain.spec.ts index c5e1ea16e..cf7d28c93 100644 --- a/packages/mongo/tests/to-plain.spec.ts +++ b/packages/mongo/tests/to-plain.spec.ts @@ -1,16 +1,15 @@ import 'jest-extended' import 'reflect-metadata'; import {ObjectID} from "mongodb"; -import {Field, IDField, MongoIdField, UUIDField} from "@marcj/marshal"; +import {f} from "@marcj/marshal"; import {mongoToPlain, partialMongoToPlain, uuid4Binary} from "../src/mapping"; test('mongo to plain', () => { class Model { - @IDField() - @MongoIdField() + @f.id().mongoId() _id?: string; - @Field() + @f date?: Date; } @@ -25,14 +24,13 @@ test('mongo to plain', () => { test('mongo to plain partial', () => { class Model { - @IDField() - @MongoIdField() + @f.id().mongoId() _id?: string; - @UUIDField() + @f.uuid() uuid?: string; - @Field() + @f date?: Date; } diff --git a/packages/mongo/tests/typeorm.spec.ts b/packages/mongo/tests/typeorm.spec.ts index 974a99a43..eb6724397 100644 --- a/packages/mongo/tests/typeorm.spec.ts +++ b/packages/mongo/tests/typeorm.spec.ts @@ -4,7 +4,7 @@ import {createConnection} from "typeorm"; import {Plan, SimpleModel, SuperSimple} from "@marcj/marshal/tests/entities"; import {getTypeOrmEntity} from "../src/typeorm"; import {PageClass} from "@marcj/marshal/tests/document-scenario/PageClass"; -import {Entity, Field, uuid, UUIDField} from "@marcj/marshal"; +import {Entity, f, getClassSchema, uuid} from "@marcj/marshal"; test('basic SuperSimple', async () => { const entity = getTypeOrmEntity(SuperSimple); @@ -34,25 +34,25 @@ test('basic inheritance', async () => { @Entity('base_file') class BaseFile { - @UUIDField() + @f.uuid() id: string = uuid(); - @Field().index() + @f.index() path: string = ''; } @Entity('file') class File extends BaseFile { - @UUIDField().optional() + @f.uuid().optional() project?: string; - @UUIDField().optional().index() + @f.uuid().optional().index() job?: string; - @Field().optional() + @f.optional() task?: string; - @Field().optional() + @f.optional() instance?: number; } @@ -86,6 +86,8 @@ test('basic PageClass', async () => { test('basic SimpleModel', async () => { const entity = getTypeOrmEntity(SimpleModel); + const schema = getClassSchema(SimpleModel); + expect(schema.getProperty('name').type).toBe('string'); expect(entity.options.indices![1]).toEqual({ columns: ['name', 'type'], diff --git a/packages/nest/tests/validation.spec.ts b/packages/nest/tests/validation.spec.ts index e58cb6859..0ad45390a 100644 --- a/packages/nest/tests/validation.spec.ts +++ b/packages/nest/tests/validation.spec.ts @@ -1,27 +1,25 @@ import 'reflect-metadata'; import 'jest-extended' -import {Optional, validate, Field} from "@marcj/marshal"; +import {validate, f} from "@marcj/marshal"; import {ValidationPipe} from '../'; import {BadRequestException} from '@nestjs/common'; test('test required', async () => { class Model { - @Field() + @f id: string = '1'; - @Field() + @f name?: string; - @Optional() + @f.optional() optional?: string; - @Optional() - @Field({String}) + @f.map(String).optional() map?: {[name: string]: string}; - @Optional() - @Field([String]) + @f.array(String).optional() array?: string[]; }