From c6830192253e532e4529c18ba680df96db47e8cd Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Mon, 11 Dec 2017 13:36:23 -0800 Subject: [PATCH] feat: Add metadata inspector --- .../src/decorators/authenticate.ts | 23 +- packages/context/src/inject.ts | 21 +- packages/metadata/src/decorator-factory.ts | 2 +- packages/metadata/src/index.ts | 1 + packages/metadata/src/inspector.ts | 166 ++++++ ...r-factory.ts => decorator-factory.test.ts} | 3 +- packages/metadata/test/unit/inspector.test.ts | 483 ++++++++++++++++++ .../test/unit/{reflect.ts => reflect.test.ts} | 2 +- packages/repository/src/decorators/model.ts | 26 +- .../test/unit/decorator/model-and-relation.ts | 65 ++- 10 files changed, 746 insertions(+), 46 deletions(-) create mode 100644 packages/metadata/src/inspector.ts rename packages/metadata/test/unit/{decorator-factory.ts => decorator-factory.test.ts} (99%) create mode 100644 packages/metadata/test/unit/inspector.test.ts rename packages/metadata/test/unit/{reflect.ts => reflect.test.ts} (99%) diff --git a/packages/authentication/src/decorators/authenticate.ts b/packages/authentication/src/decorators/authenticate.ts index 5a0a084608e0..75ce842bbd7c 100644 --- a/packages/authentication/src/decorators/authenticate.ts +++ b/packages/authentication/src/decorators/authenticate.ts @@ -3,7 +3,11 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {Reflector, Constructor} from '@loopback/context'; +import { + MetadataInspector, + Constructor, + MethodDecoratorFactory, +} from '@loopback/context'; import {AuthenticationBindings} from '../keys'; /** @@ -21,18 +25,13 @@ export interface AuthenticationMetadata { * @param options Additional options to configure the authentication. */ export function authenticate(strategyName: string, options?: Object) { - return function(controllerClass: Object, methodName: string) { - const metadataObj: AuthenticationMetadata = { + return MethodDecoratorFactory.createDecorator( + AuthenticationBindings.METADATA, + { strategy: strategyName, options: options || {}, - }; - Reflector.defineMetadata( - AuthenticationBindings.METADATA, - metadataObj, - controllerClass, - methodName, - ); - }; + }, + ); } /** @@ -45,7 +44,7 @@ export function getAuthenticateMetadata( controllerClass: Constructor<{}>, methodName: string, ): AuthenticationMetadata | undefined { - return Reflector.getMetadata( + return MetadataInspector.getMethodMetadata( AuthenticationBindings.METADATA, controllerClass.prototype, methodName, diff --git a/packages/context/src/inject.ts b/packages/context/src/inject.ts index 19cd730f474f..8631acdc7bd5 100644 --- a/packages/context/src/inject.ts +++ b/packages/context/src/inject.ts @@ -4,9 +4,10 @@ // License text available at https://opensource.org/licenses/MIT import { - Reflector, + MetadataInspector, ParameterDecoratorFactory, PropertyDecoratorFactory, + MetadataMap, } from '@loopback/metadata'; import {BoundValue, ValueOrPromise} from './binding'; import {Context} from './context'; @@ -186,9 +187,12 @@ export function describeInjectedArguments( method?: string | symbol, ): Injection[] { method = method || ''; - const meta = Reflector.getMetadata(PARAMETERS_KEY, target); - if (meta == null) return []; - return meta[method] || []; + const meta = MetadataInspector.getAllParameterMetadata( + PARAMETERS_KEY, + target, + method, + ); + return meta || []; } /** @@ -199,8 +203,11 @@ export function describeInjectedArguments( export function describeInjectedProperties( // tslint:disable-next-line:no-any target: any, -): {[p: string]: Injection} { - const metadata: {[name: string]: Injection} = - Reflector.getMetadata(PROPERTIES_KEY, target) || {}; +): MetadataMap { + const metadata = + MetadataInspector.getAllPropertyMetadata( + PROPERTIES_KEY, + target, + ) || {}; return metadata; } diff --git a/packages/metadata/src/decorator-factory.ts b/packages/metadata/src/decorator-factory.ts index 820a683d19e7..0a97b0bdc156 100644 --- a/packages/metadata/src/decorator-factory.ts +++ b/packages/metadata/src/decorator-factory.ts @@ -6,7 +6,7 @@ import {Reflector} from './reflect'; import * as _ from 'lodash'; import * as debugModule from 'debug'; -const debug = debugModule('loopback:decorator'); +const debug = debugModule('loopback:metadata:decorator'); // tslint:disable:no-any diff --git a/packages/metadata/src/index.ts b/packages/metadata/src/index.ts index dbe621fc1ffc..b5c4aee6c85e 100644 --- a/packages/metadata/src/index.ts +++ b/packages/metadata/src/index.ts @@ -5,3 +5,4 @@ export * from './reflect'; export * from './decorator-factory'; +export * from './inspector'; diff --git a/packages/metadata/src/inspector.ts b/packages/metadata/src/inspector.ts new file mode 100644 index 000000000000..192cd640e861 --- /dev/null +++ b/packages/metadata/src/inspector.ts @@ -0,0 +1,166 @@ +// Copyright IBM Corp. 2013,2017. All Rights Reserved. +// Node module: @loopback/metadata +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +import {Reflector, NamespacedReflect} from './reflect'; +import {MetadataMap} from './decorator-factory'; + +const TSReflector = new NamespacedReflect(); + +/** + * Design time metadata for a method + */ +export interface DesignTimeMethodMetadata { + type: Function; + parameterTypes: Function[]; + returnType: Function; +} + +/** + * Inspector for metadata applied by decorators + */ +export class MetadataInspector { + /** + * Get the metadata associated with the given key for a given class + * @param key Metadata key + * @param target Class that contains the metadata + */ + static getClassMetadata(key: string, target: Function): T | undefined { + return Reflector.getMetadata(key, target); + } + + /** + * Get the metadata associated with the given key for all methods of the + * target class or prototype + * @param key Metadata key + * @param target Class for static methods or prototype for instance methods + */ + static getAllMethodMetadata( + key: string, + target: Object, + ): MetadataMap | undefined { + return Reflector.getMetadata(key, target); + } + + /** + * Get the metadata associated with the given key for a given method of the + * target class or prototype + * @param key Metadata key + * @param target Class for static methods or prototype for instance methods + * @param methodName Method name. If not present, default to '' to use + * the constructor + */ + static getMethodMetadata( + key: string, + target: Object, + methodName?: string | symbol, + ): T | undefined { + methodName = methodName || ''; + const meta: MetadataMap = Reflector.getMetadata(key, target); + return meta && meta[methodName]; + } + + /** + * Get the metadata associated with the given key for all properties of the + * target class or prototype + * @param key Metadata key + * @param target Class for static methods or prototype for instance methods + */ + static getAllPropertyMetadata( + key: string, + target: Object, + ): MetadataMap | undefined { + return Reflector.getMetadata(key, target); + } + + /** + * Get the metadata associated with the given key for a given property of the + * target class or prototype + * @param key Metadata key + * @param target Class for static properties or prototype for instance + * properties + * @param propertyName Property name + */ + static getPropertyMetadata( + key: string, + target: Object, + propertyName: string | symbol, + ): T | undefined { + const meta: MetadataMap = Reflector.getMetadata(key, target); + return meta && meta[propertyName]; + } + + /** + * Get the metadata associated with the given key for all parameters of a + * given method + * @param key Metadata key + * @param target Class for static methods or prototype for instance methods + * @param methodName Method name. If not present, default to '' to use + * the constructor + */ + static getAllParameterMetadata( + key: string, + target: Object, + methodName?: string | symbol, + ): T[] | undefined { + methodName = methodName || ''; + const meta: MetadataMap = Reflector.getMetadata(key, target); + return meta && meta[methodName]; + } + + /** + * Get the metadata associated with the given key for a parameter of a given + * method by index + * @param key Metadata key + * @param target Class for static methods or prototype for instance methods + * @param methodName Method name. If not present, default to '' to use + * the constructor + * @param index Index of the parameter, starting with 0 + */ + static getParameterMetadata( + key: string, + target: Object, + methodName: string | symbol, + index: number, + ): T | undefined { + methodName = methodName || ''; + const meta: MetadataMap = Reflector.getMetadata(key, target); + const params = meta && meta[methodName]; + return params && params[index]; + } + + /** + * Get TypeScript design time type for the property + * @param target Class or prototype + * @param propertyName Property name + */ + static getDesignTypeForProperty( + target: Object, + propertyName: string | symbol, + ): Function { + return TSReflector.getMetadata('design:type', target, propertyName); + } + + static getDesignTypeForMethod( + target: Object, + methodName: string | symbol, + ): DesignTimeMethodMetadata { + const type = TSReflector.getMetadata('design:type', target, methodName); + const parameterTypes = TSReflector.getMetadata( + 'design:paramtypes', + target, + methodName, + ); + const returnType = TSReflector.getMetadata( + 'design:returntype', + target, + methodName, + ); + return { + type, + parameterTypes, + returnType, + }; + } +} diff --git a/packages/metadata/test/unit/decorator-factory.ts b/packages/metadata/test/unit/decorator-factory.test.ts similarity index 99% rename from packages/metadata/test/unit/decorator-factory.ts rename to packages/metadata/test/unit/decorator-factory.test.ts index f9aee2357c1f..2beb109dc0a6 100644 --- a/packages/metadata/test/unit/decorator-factory.ts +++ b/packages/metadata/test/unit/decorator-factory.test.ts @@ -5,7 +5,6 @@ import {expect} from '@loopback/testlab'; import { - Reflector, ClassDecoratorFactory, PropertyDecoratorFactory, MethodDecoratorFactory, @@ -13,6 +12,8 @@ import { DecoratorFactory, } from '../..'; +import {Reflector} from '../../src/reflect'; + describe('ClassDecoratorFactory', () => { /** * Define `@classDecorator(spec)` diff --git a/packages/metadata/test/unit/inspector.test.ts b/packages/metadata/test/unit/inspector.test.ts new file mode 100644 index 000000000000..e8fa577b8e08 --- /dev/null +++ b/packages/metadata/test/unit/inspector.test.ts @@ -0,0 +1,483 @@ +// Copyright IBM Corp. 2013,2017. All Rights Reserved. +// Node module: @loopback/metadata +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +import {expect} from '@loopback/testlab'; +import { + ClassDecoratorFactory, + PropertyDecoratorFactory, + MethodDecoratorFactory, + ParameterDecoratorFactory, + MetadataInspector, +} from '../..'; + +describe('Inspector for a class', () => { + /** + * Define `@classDecorator(spec)` + * @param spec + */ + function classDecorator(spec: object): ClassDecorator { + return ClassDecoratorFactory.createDecorator('test', spec); + } + + @classDecorator({x: 1}) + class BaseController {} + + @classDecorator({y: 2}) + class SubController extends BaseController {} + + class AnotherController extends BaseController {} + + it('inspects metadata of a base class', () => { + const meta = MetadataInspector.getClassMetadata('test', BaseController); + expect(meta).to.eql({x: 1}); + }); + + it('inspects metadata of a sub class', () => { + const meta = MetadataInspector.getClassMetadata('test', SubController); + expect(meta).to.eql({x: 1, y: 2}); + }); + + it('inspects metadata of a sub class without override', () => { + const meta = MetadataInspector.getClassMetadata('test', AnotherController); + expect(meta).to.eql({x: 1}); + }); +}); + +describe('Inspector for instance properties', () => { + /** + * Define `@propertyDecorator(spec)` + * @param spec + */ + function propertyDecorator(spec: object): PropertyDecorator { + return PropertyDecoratorFactory.createDecorator('test', spec); + } + + class BaseController { + @propertyDecorator({x: 1}) + myProp: string; + } + + class SubController extends BaseController { + @propertyDecorator({y: 2}) + myProp: string; + } + + it('inspects metadata of all properties of a base class', () => { + const meta = MetadataInspector.getAllPropertyMetadata( + 'test', + BaseController.prototype, + ); + expect(meta).to.eql({myProp: {x: 1}}); + }); + + it('inspects metadata of a property of a base class', () => { + const meta = MetadataInspector.getPropertyMetadata( + 'test', + BaseController.prototype, + 'myProp', + ); + expect(meta).to.eql({x: 1}); + }); + + it('inspects metadata of all properties of a sub class', () => { + const meta = MetadataInspector.getAllPropertyMetadata( + 'test', + SubController.prototype, + ); + expect(meta).to.eql({myProp: {x: 1, y: 2}}); + }); +}); + +describe('Inspector for static properties', () => { + /** + * Define `@propertyDecorator(spec)` + * @param spec + */ + function propertyDecorator(spec: object): PropertyDecorator { + return PropertyDecoratorFactory.createDecorator('test', spec); + } + + class BaseController { + @propertyDecorator({x: 1}) + static myProp: string; + } + + class SubController extends BaseController { + @propertyDecorator({y: 2}) + static myProp: string; + } + + it('inspects metadata of all properties of a base class', () => { + const meta = MetadataInspector.getAllPropertyMetadata( + 'test', + BaseController, + ); + expect(meta).to.eql({myProp: {x: 1}}); + }); + + it('inspects metadata of a property of a base class', () => { + const meta = MetadataInspector.getPropertyMetadata( + 'test', + BaseController, + 'myProp', + ); + expect(meta).to.eql({x: 1}); + }); + + it('inspects metadata of all properties of a sub class', () => { + const meta = MetadataInspector.getAllPropertyMetadata( + 'test', + SubController, + ); + expect(meta).to.eql({myProp: {x: 1, y: 2}}); + }); +}); + +describe('Inspector for instance methods', () => { + /** + * Define `@methodDecorator(spec)` + * @param spec + */ + function methodDecorator(spec: object): MethodDecorator { + return MethodDecoratorFactory.createDecorator('test', spec); + } + + class BaseController { + @methodDecorator({x: 1}) + myMethod() {} + } + + class SubController extends BaseController { + @methodDecorator({y: 2}) + myMethod() {} + } + + it('inspects metadata of all methods of a base class', () => { + const meta = MetadataInspector.getAllMethodMetadata( + 'test', + BaseController.prototype, + ); + expect(meta).to.eql({myMethod: {x: 1}}); + }); + + it('inspects metadata of a method of a base class', () => { + const meta = MetadataInspector.getMethodMetadata( + 'test', + BaseController.prototype, + 'myMethod', + ); + expect(meta).to.eql({x: 1}); + }); + + it('inspects metadata of all methods of a sub class', () => { + const meta = MetadataInspector.getAllMethodMetadata( + 'test', + SubController.prototype, + ); + expect(meta).to.eql({myMethod: {x: 1, y: 2}}); + }); +}); + +describe('Inspector for static methods', () => { + /** + * Define `@methodDecorator(spec)` + * @param spec + */ + function methodDecorator(spec: object): MethodDecorator { + return PropertyDecoratorFactory.createDecorator('test', spec); + } + + class BaseController { + @methodDecorator({x: 1}) + static myMethod() {} + } + + class SubController extends BaseController { + @methodDecorator({y: 2}) + static myMethod() {} + } + + it('inspects metadata of all methods of a base class', () => { + const meta = MetadataInspector.getAllMethodMetadata('test', BaseController); + expect(meta).to.eql({myMethod: {x: 1}}); + }); + + it('inspects metadata of a property of a base class', () => { + const meta = MetadataInspector.getMethodMetadata( + 'test', + BaseController, + 'myMethod', + ); + expect(meta).to.eql({x: 1}); + }); + + it('inspects metadata of all properties of a sub class', () => { + const meta = MetadataInspector.getAllMethodMetadata('test', SubController); + expect(meta).to.eql({myMethod: {x: 1, y: 2}}); + }); +}); + +describe('Inspector for parameters of an instance method', () => { + /** + * Define `@parameterDecorator(spec)` + * @param spec + */ + function parameterDecorator(spec: object): ParameterDecorator { + return ParameterDecoratorFactory.createDecorator('test', spec); + } + + class BaseController { + myMethod( + @parameterDecorator({x: 1}) + a: string, + b: number, + ) {} + } + + class SubController extends BaseController { + myMethod( + @parameterDecorator({y: 2}) + a: string, + @parameterDecorator({x: 2}) + b: number, + ) {} + } + + it('inspects metadata of all parameters of a method of the base class', () => { + const meta = MetadataInspector.getAllParameterMetadata( + 'test', + BaseController.prototype, + 'myMethod', + ); + expect(meta).to.eql([{x: 1}, undefined]); + }); + + it('inspects metadata of all parameters of a method of the sub class', () => { + const meta = MetadataInspector.getAllParameterMetadata( + 'test', + SubController.prototype, + 'myMethod', + ); + expect(meta).to.eql([{x: 1, y: 2}, {x: 2}]); + }); + + it('inspects metadata of a parameter of a method of the sub class', () => { + const meta = MetadataInspector.getParameterMetadata( + 'test', + SubController.prototype, + 'myMethod', + 0, + ); + expect(meta).to.eql({x: 1, y: 2}); + }); +}); + +describe('Inspector for parameters of a static method', () => { + /** + * Define `@parameterDecorator(spec)` + * @param spec + */ + function parameterDecorator(spec: object): ParameterDecorator { + return ParameterDecoratorFactory.createDecorator('test', spec); + } + + class BaseController { + static myMethod( + @parameterDecorator({x: 1}) + a: string, + b: number, + ) {} + } + + class SubController extends BaseController { + static myMethod( + @parameterDecorator({y: 2}) + a: string, + @parameterDecorator({x: 2}) + b: number, + ) {} + } + + it('inspects metadata of all parameters of a method of the base class', () => { + const meta = MetadataInspector.getAllParameterMetadata( + 'test', + BaseController, + 'myMethod', + ); + expect(meta).to.eql([{x: 1}, undefined]); + }); + + it('inspects metadata of all parameters of a method of the sub class', () => { + const meta = MetadataInspector.getAllParameterMetadata( + 'test', + SubController, + 'myMethod', + ); + expect(meta).to.eql([{x: 1, y: 2}, {x: 2}]); + }); + + it('inspects metadata of a parameter of a method of the sub class', () => { + const meta = MetadataInspector.getParameterMetadata( + 'test', + SubController, + 'myMethod', + 0, + ); + expect(meta).to.eql({x: 1, y: 2}); + }); +}); + +describe('Inspector for parameters of a constructor', () => { + /** + * Define `@parameterDecorator(spec)` + * @param spec + */ + function parameterDecorator(spec: object): ParameterDecorator { + return ParameterDecoratorFactory.createDecorator('test', spec); + } + + class BaseController { + constructor( + @parameterDecorator({x: 1}) + a: string, + b: number, + ) {} + } + + class SubController extends BaseController { + constructor( + @parameterDecorator({y: 2}) + a: string, + @parameterDecorator({x: 2}) + b: number, + ) { + super(a, b); + } + } + + it('inspects metadata of all parameters of the constructor of the base class', () => { + const meta = MetadataInspector.getAllParameterMetadata( + 'test', + BaseController, + ); + expect(meta).to.eql([{x: 1}, undefined]); + }); + + it('inspects metadata of all parameters of the constructor of the sub class', () => { + const meta = MetadataInspector.getAllParameterMetadata( + 'test', + SubController, + '', + ); + expect(meta).to.eql([{x: 1, y: 2}, {x: 2}]); + }); + + it('inspects metadata of a parameter of the constructor of the sub class', () => { + const meta = MetadataInspector.getParameterMetadata( + 'test', + SubController, + '', + 0, + ); + expect(meta).to.eql({x: 1, y: 2}); + }); +}); + +describe('Inspector for design time metadata', () => { + function propertyDecorator(spec?: object): PropertyDecorator { + return PropertyDecoratorFactory.createDecorator('test:properties', spec); + } + + function methodDecorator(spec?: object): MethodDecorator { + return MethodDecoratorFactory.createDecorator('test:methods', spec); + } + + function parameterDecorator(spec?: object): ParameterDecorator { + return ParameterDecoratorFactory.createDecorator('test:parameters', spec); + } + + class MyClass {} + + class MyController { + constructor( + @parameterDecorator({x: 1}) + a: string, + b: number, + ) {} + + @propertyDecorator() myProp: string; + + @propertyDecorator() myType: MyClass; + + @methodDecorator() + myMethod(x: string, y: number): boolean { + return false; + } + + @propertyDecorator() static myStaticProp = {}; + + @methodDecorator() + static myStaticMethod(x: string, y: number): boolean { + return false; + } + } + + it('inspects design time type for properties with simple type', () => { + const meta = MetadataInspector.getDesignTypeForProperty( + MyController.prototype, + 'myProp', + ); + expect(meta).to.eql(String); + }); + + it('inspects design time type for properties with class type', () => { + const meta = MetadataInspector.getDesignTypeForProperty( + MyController.prototype, + 'myType', + ); + expect(meta).to.eql(MyClass); + }); + + it('inspects design time type for static properties', () => { + const meta = MetadataInspector.getDesignTypeForProperty( + MyController, + 'myStaticProp', + ); + expect(meta).to.eql(Object); + }); + + it('inspects design time type for the constructor', () => { + const meta = MetadataInspector.getDesignTypeForMethod(MyController, ''); + expect(meta).to.eql({ + type: undefined, + returnType: undefined, + parameterTypes: [String, Number], + }); + }); + + it('inspects design time type for instance methods', () => { + const meta = MetadataInspector.getDesignTypeForMethod( + MyController.prototype, + 'myMethod', + ); + expect(meta).to.eql({ + type: Function, + returnType: Boolean, + parameterTypes: [String, Number], + }); + }); + + it('inspects design time type for static methods', () => { + const meta = MetadataInspector.getDesignTypeForMethod( + MyController, + 'myStaticMethod', + ); + expect(meta).to.eql({ + type: Function, + returnType: Boolean, + parameterTypes: [String, Number], + }); + }); +}); diff --git a/packages/metadata/test/unit/reflect.ts b/packages/metadata/test/unit/reflect.test.ts similarity index 99% rename from packages/metadata/test/unit/reflect.ts rename to packages/metadata/test/unit/reflect.test.ts index 0259751ae2e8..f2edd5793f67 100644 --- a/packages/metadata/test/unit/reflect.ts +++ b/packages/metadata/test/unit/reflect.test.ts @@ -4,7 +4,7 @@ // License text available at https://opensource.org/licenses/MIT import {expect} from '@loopback/testlab'; -import {NamespacedReflect, Reflector} from '../..'; +import {NamespacedReflect, Reflector} from '../../src/reflect'; import 'reflect-metadata'; function givenReflectContextWithNameSpace(): NamespacedReflect { diff --git a/packages/repository/src/decorators/model.ts b/packages/repository/src/decorators/model.ts index 6e2809f4e5ef..f25ca774d0e6 100644 --- a/packages/repository/src/decorators/model.ts +++ b/packages/repository/src/decorators/model.ts @@ -4,9 +4,10 @@ // License text available at https://opensource.org/licenses/MIT import { - Reflector, + MetadataInspector, ClassDecoratorFactory, PropertyDecoratorFactory, + MetadataMap, } from '@loopback/context'; import {ModelDefinition, ModelDefinitionSyntax} from '../model'; import {PropertyDefinition} from '../index'; @@ -14,7 +15,7 @@ import {PropertyDefinition} from '../index'; export const MODEL_KEY = 'loopback:model'; export const MODEL_PROPERTIES_KEY = 'loopback:model-properties'; -type PropertyMap = {[name: string]: PropertyDefinition}; +type PropertyMap = MetadataMap; // tslint:disable:no-any @@ -39,13 +40,22 @@ export function model(definition?: ModelDefinitionSyntax) { // Build "ModelDefinition" and store it on model constructor const modelDef = new ModelDefinition(definition); - const propertyMap: PropertyMap = Reflector.getMetadata( - MODEL_PROPERTIES_KEY, - target.prototype, - ); + const propertyMap: PropertyMap = + MetadataInspector.getAllPropertyMetadata( + MODEL_PROPERTIES_KEY, + target.prototype, + ) || {}; for (const p in propertyMap) { - modelDef.addProperty(p, propertyMap[p]); + const propertyDef = propertyMap[p]; + const designType = MetadataInspector.getDesignTypeForProperty( + target.prototype, + p, + ); + if (!propertyDef.type) { + propertyDef.type = designType; + } + modelDef.addProperty(p, propertyDef); } target.definition = modelDef; @@ -57,7 +67,7 @@ export function model(definition?: ModelDefinitionSyntax) { * @param definition * @returns {(target:any, key:string)} */ -export function property(definition: PropertyDefinition) { +export function property(definition: Partial) { return PropertyDecoratorFactory.createDecorator( MODEL_PROPERTIES_KEY, definition, diff --git a/packages/repository/test/unit/decorator/model-and-relation.ts b/packages/repository/test/unit/decorator/model-and-relation.ts index effa9affb1a1..315f38e4bdd6 100644 --- a/packages/repository/test/unit/decorator/model-and-relation.ts +++ b/packages/repository/test/unit/decorator/model-and-relation.ts @@ -19,7 +19,7 @@ import { } from '../../../'; import {Entity, ValueObject} from '../../../'; -import {Reflector} from '@loopback/context'; +import {MetadataInspector} from '@loopback/context'; describe('model decorator', () => { @model() @@ -54,7 +54,6 @@ describe('model decorator', () => { @model({name: 'order'}) class Order extends Entity { @property({ - type: 'number', mysql: { column: 'QTY', }, @@ -96,60 +95,86 @@ describe('model decorator', () => { // Skip the tests before we resolve the issue around global `Reflector` // The tests are passing it run alone but fails with `npm test` it('adds model metadata', () => { - const meta = Reflector.getOwnMetadata(MODEL_KEY, Order); + const meta = MetadataInspector.getClassMetadata(MODEL_KEY, Order); expect(meta).to.eql({name: 'order'}); }); it('adds property metadata', () => { - const meta = Reflector.getOwnMetadata( - MODEL_PROPERTIES_KEY, - Order.prototype, - ); + const meta = + MetadataInspector.getAllPropertyMetadata( + MODEL_PROPERTIES_KEY, + Order.prototype, + ) || {}; expect(meta.quantity).to.eql({ - type: 'number', + type: Number, mysql: { column: 'QTY', }, }); + expect(meta.id).to.eql({type: 'string', id: true, generated: true}); }); it('adds embedsOne metadata', () => { - const meta = Reflector.getOwnMetadata(RELATIONS_KEY, Customer.prototype); + const meta = + MetadataInspector.getAllPropertyMetadata( + RELATIONS_KEY, + Customer.prototype, + ) || {}; expect(meta.address).to.eql({ type: RelationType.embedsOne, }); }); it('adds embedsMany metadata', () => { - const meta = Reflector.getOwnMetadata(RELATIONS_KEY, Customer.prototype); + const meta = + MetadataInspector.getAllPropertyMetadata( + RELATIONS_KEY, + Customer.prototype, + ) || {}; expect(meta.phones).to.eql({ type: RelationType.embedsMany, }); }); it('adds referencesMany metadata', () => { - const meta = Reflector.getOwnMetadata(RELATIONS_KEY, Customer.prototype); + const meta = + MetadataInspector.getAllPropertyMetadata( + RELATIONS_KEY, + Customer.prototype, + ) || {}; expect(meta.accounts).to.eql({ type: RelationType.referencesMany, }); }); it('adds referencesOne metadata', () => { - const meta = Reflector.getOwnMetadata(RELATIONS_KEY, Customer.prototype); + const meta = + MetadataInspector.getAllPropertyMetadata( + RELATIONS_KEY, + Customer.prototype, + ) || {}; expect(meta.profile).to.eql({ type: RelationType.referencesOne, }); }); it('adds hasMany metadata', () => { - const meta = Reflector.getOwnMetadata(RELATIONS_KEY, Customer.prototype); + const meta = + MetadataInspector.getAllPropertyMetadata( + RELATIONS_KEY, + Customer.prototype, + ) || {}; expect(meta.orders).to.eql({ type: RelationType.hasMany, }); }); it('adds belongsTo metadata', () => { - const meta = Reflector.getOwnMetadata(RELATIONS_KEY, Order.prototype); + const meta = + MetadataInspector.getAllPropertyMetadata( + RELATIONS_KEY, + Order.prototype, + ) || {}; expect(meta.customer).to.eql({ type: RelationType.belongsTo, target: 'Customer', @@ -157,14 +182,22 @@ describe('model decorator', () => { }); it('adds hasOne metadata', () => { - const meta = Reflector.getOwnMetadata(RELATIONS_KEY, Customer.prototype); + const meta = + MetadataInspector.getAllPropertyMetadata( + RELATIONS_KEY, + Customer.prototype, + ) || {}; expect(meta.lastOrder).to.eql({ type: RelationType.hasOne, }); }); it('adds relation metadata', () => { - const meta = Reflector.getOwnMetadata(RELATIONS_KEY, Customer.prototype); + const meta = + MetadataInspector.getAllPropertyMetadata( + RELATIONS_KEY, + Customer.prototype, + ) || {}; expect(meta.recentOrders).to.eql({ type: RelationType.hasMany, });