Skip to content

Commit

Permalink
feat(repository): helper function for getting Model metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
Kevin Delisle committed Jan 2, 2018
1 parent 59bbfb6 commit b19635d
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
39 changes: 39 additions & 0 deletions packages/repository/src/decorators/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {InspectionOptions, MetadataInspector} from '@loopback/context';
import {MODEL_PROPERTIES_KEY, MODEL_WITH_PROPERTIES_KEY} from '../';
import {ModelDefinition, PropertyDefinition} from '../../index';
export class ModelMetadataHelper {
/**
* A utility function to simplify retrieving metadata from a target model and
* its properties.
* @param target The class from which to retrieve metadata.
* @param options An options object for the MetadataInspector to customize
* the output of the metadata retrieval functions.
*/
static getModelMetadata(target: Function, options?: InspectionOptions) {
let classDef = MetadataInspector.getClassMetadata(
MODEL_WITH_PROPERTIES_KEY,
target,
options,
);
// Return the cached value, if it exists.
// XXX(kjdelisle): If we're going to support dynamic updates, then this
// will be problematic in the future, since it will never update.
if (classDef) {
return classDef;
} else {
const meta = new ModelDefinition(
Object.assign({name: target.name}, classDef),
);
meta.properties = Object.assign(
<PropertyDefinition>{},
MetadataInspector.getAllPropertyMetadata(
MODEL_PROPERTIES_KEY,
target.prototype,
options,
),
);
MetadataInspector.defineMetadata(MODEL_WITH_PROPERTIES_KEY, meta, target);
return meta;
}
}
}
1 change: 1 addition & 0 deletions packages/repository/src/decorators/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {

export const MODEL_KEY = 'loopback:model';
export const MODEL_PROPERTIES_KEY = 'loopback:model-properties';
export const MODEL_WITH_PROPERTIES_KEY = 'loopback:model-and-properties';

type PropertyMap = MetadataMap<PropertyDefinition>;

Expand Down
1 change: 1 addition & 0 deletions packages/repository/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './common-types';
export * from './decorators/model';
export * from './decorators/repository';
export * from './decorators/relation';
export * from './decorators/metadata';
export * from './types';
export * from './model';
export * from './query';
Expand Down
93 changes: 93 additions & 0 deletions packages/repository/test/unit/decorator/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {ModelMetadataHelper} from '../../../src';
import {
property,
model,
ModelDefinition,
MODEL_KEY,
MODEL_WITH_PROPERTIES_KEY,
} from '../../..';
import {expect} from '@loopback/testlab';
import {MetadataInspector} from '@loopback/context';

describe('Repository', () => {
describe('getAllClassMetadata', () => {
@model()
class Colour {
@property({})
rgb: string;
}
@model()
class Widget {
@property() id: number;
@property.array(Colour) colours: Colour[];
}

@model()
class Samoflange {
id: number;
name: string;
canRotate: boolean;
}

@model()
class Phlange {
@property() id: number;
@property() canFlap: boolean;
@property.array(Colour) colours: Colour[];
}

it('retrieves metadata for classes with @model', () => {
const meta = ModelMetadataHelper.getModelMetadata(Samoflange);
expect(meta).to.deepEqual(
new ModelDefinition({
name: 'Samoflange',
properties: {},
settings: new Map(),
}),
);
});

it('retrieves metadata for classes with @model and @property', () => {
const meta = ModelMetadataHelper.getModelMetadata(Widget);
expect(meta).to.deepEqual(
new ModelDefinition({
properties: {
id: {
type: Number,
},
colours: {
array: true,
type: Colour,
},
},
settings: new Map(),
name: 'Widget',
}),
);
});

it('returns cached metadata instead of recreating it', () => {
const classMeta = MetadataInspector.getClassMetadata(
MODEL_KEY,
Phlange,
) as ModelDefinition;
classMeta.properties = {
foo: {
type: String,
},
};
// Intentionally change the metadata to be different from the Phlange
// class metadata
MetadataInspector.defineMetadata(
MODEL_WITH_PROPERTIES_KEY,
classMeta,
Phlange,
);

const meta = ModelMetadataHelper.getModelMetadata(
Phlange,
) as ModelDefinition;
expect(meta.properties).to.eql(classMeta.properties);
});
});
});

0 comments on commit b19635d

Please sign in to comment.