diff --git a/packages/repository/src/model.ts b/packages/repository/src/model.ts index 96e595cb88fc..dc953a6afb07 100644 --- a/packages/repository/src/model.ts +++ b/packages/repository/src/model.ts @@ -106,7 +106,9 @@ export class ModelDefinition { if (ids) { return ids.map(id => this.properties[id]); } - return Object.values(this.properties).filter(prop => prop.id); + const idProps = Object.keys(this.properties) + .map(p => this.properties[p]).filter(prop => prop.id); + return idProps; } } @@ -122,7 +124,8 @@ export abstract class Model { */ toJSON(): Object { const json: AnyObject = {}; - for (const p in this.definition.properties) { + const def = ( this.constructor).definition; + for (const p in def.properties) { if (p in this) { json[p] = this[p]; } @@ -134,12 +137,14 @@ export abstract class Model { * Convert to a plain object as DTO */ toObject(options?: Options): Object { - const obj: AnyObject = {}; - if (options && options.ignoreUnknownProperties === false) - for (const p in this.definition.properties) { - if (p in this) { + let obj: AnyObject; + if (options && options.ignoreUnknownProperties === false) { + obj = {}; + for (const p in this) { obj[p] = this[p]; } + } else { + obj = this.toJSON(); } return obj; } @@ -200,9 +205,10 @@ export abstract class Entity extends Model implements Persistable { const idProps = this.definition.idProperties(); if (idProps.length === 1) { where[idProps[0].name] = id; - } - for (const idProp of idProps) { - where[idProp.name] = id[idProp.name]; + } else { + for (const idProp of idProps) { + where[idProp.name] = id[idProp.name]; + } } return where; } diff --git a/packages/repository/test/unit/decorator/model-and-relation.ts b/packages/repository/test/unit/decorator/model-and-relation.ts index d9ef5a541f12..ee814ef4c545 100644 --- a/packages/repository/test/unit/decorator/model-and-relation.ts +++ b/packages/repository/test/unit/decorator/model-and-relation.ts @@ -6,7 +6,7 @@ import { expect } from '@loopback/testlab'; import { model, property, MODEL_KEY, PROPERTY_KEY } from '../../../src/decorators/model'; -import { belongsTo, embedsOne, embedsMany, hasMany, referencesMany, +import { relation, hasOne, belongsTo, embedsOne, embedsMany, hasMany, referencesMany, referencesOne, RELATION_KEY, RelationType } from '../../../src/decorators/relation'; import { Entity, ValueObject } from '../../../src/model'; @@ -40,8 +40,27 @@ describe('model decorator', () => { description: string; } + interface ICustomer {} + + @model({ name: 'order' }) + class Order extends Entity { + @property({ + name: 'qty', mysql: { + column: 'QTY', + }, + }) + quantity: number; + + @property({ name: 'id', id: true, generated: true }) + id: string; + customerId: string; + + @belongsTo({ target: 'Customer' }) + customer: ICustomer; // TypeScript does not allow me to reference Customer here + } + @model() - class Customer extends Entity { + class Customer extends Entity implements ICustomer { id: string; email: string; firstName: string; @@ -61,23 +80,12 @@ describe('model decorator', () => { @hasMany() orders?: Order[]; - } - - @model({ name: 'order' }) - class Order extends Entity { - @property({ - name: 'qty', mysql: { - column: 'QTY', - }, - }) - quantity: number; - @property({ name: 'id', id: true, generated: true }) - id: string; - customerId: string; + @hasOne() + lastOrder?: Order; - @belongsTo({ target: 'Customer' }) - customer: Customer; + @relation({type: RelationType.hasMany}) + recentOrders?: Order[]; } // Skip the tests before we resolve the issue around global `Reflector` @@ -139,4 +147,18 @@ describe('model decorator', () => { }); }); + it('adds hasOne metadata', () => { + const meta = Reflector.getOwnMetadata(RELATION_KEY, Customer.prototype, 'lastOrder'); + expect(meta).to.eql({ + type: RelationType.hasOne, + }); + }); + + it('adds relation metadata', () => { + const meta = Reflector.getOwnMetadata(RELATION_KEY, Customer.prototype, 'recentOrders'); + expect(meta).to.eql({ + type: RelationType.hasMany, + }); + }); + }); diff --git a/packages/repository/test/unit/model/model.ts b/packages/repository/test/unit/model/model.ts index 5484fb2647a3..1ff9fd315711 100644 --- a/packages/repository/test/unit/model/model.ts +++ b/packages/repository/test/unit/model/model.ts @@ -9,22 +9,118 @@ import { Model, Entity, ModelDefinition, PropertyDefinition } from '../../../src describe('model', () => { + const customerDef = new ModelDefinition('Customer'); + customerDef.addProperty(new PropertyDefinition('id', 'string')) + .addProperty('email', 'string').addProperty('firstName', String) + .addProperty('lastName', STRING) + .addSetting('id', 'id'); + + const relamCustomerDef = new ModelDefinition('RealmCustomer'); + relamCustomerDef.addProperty(new PropertyDefinition('realm', 'string')) + .addProperty('email', 'string').addProperty('firstName', String) + .addProperty('lastName', STRING) + .addSetting('id', ['realm', 'email']); + + const userDef = new ModelDefinition('User'); + const idProp = new PropertyDefinition('id', 'string'); + idProp.id = true; + userDef.addProperty(idProp) + .addProperty('email', 'string').addProperty('firstName', String) + .addProperty('lastName', STRING); + + class Customer extends Entity { + static modelName = 'Customer'; + static definition = customerDef; + } + + class RealmCustomer extends Entity { + static modelName = 'RealmCustomer'; + static definition = relamCustomerDef; + } + + class User extends Entity { + static modelName = 'User'; + static definition = userDef; + } + + function createCustomer() { + const customer = new Customer(); + customer.id = '123'; + customer.email = 'xyz@example.com'; + return customer; + } + + function createRealmCustomer() { + const customer = new RealmCustomer(); + customer.realm = 'org1'; + customer.email = 'xyz@example.com'; + return customer; + } + it('adds properties', () => { - const modelDef = new ModelDefinition('Customer'); - modelDef.addProperty(new PropertyDefinition('id', 'string')) - .addProperty('email', 'string').addProperty('firstName', String) - .addProperty('lastName', STRING) - .addSetting('key', ['id']); - expect(modelDef.name).to.eql('Customer'); - expect(modelDef.properties).have.properties('id', 'email', 'lastName', 'firstName'); - expect(modelDef.properties.lastName).to.eql( + expect(customerDef.name).to.eql('Customer'); + expect(customerDef.properties).have.properties('id', 'email', 'lastName', 'firstName'); + expect(customerDef.properties.lastName).to.eql( new PropertyDefinition('lastName', STRING)); }); - /* - class CustomerModel extends Entity { - static modelName = 'Customer'; - static definition = modelDef; - } - */ + it('adds settings', () => { + expect(customerDef.settings).have.property('id', 'id'); + }); + + it('lists id properties', () => { + expect(customerDef.idProperties()).to.eql([customerDef.properties.id]); + expect(userDef.idProperties()).to.eql([userDef.properties.id]); + expect(relamCustomerDef.idProperties()).to.eql([ + relamCustomerDef.properties.realm, + relamCustomerDef.properties.email, + ]); + }); + + it('converts to json', () => { + const customer = createCustomer(); + expect(customer.toJSON()).to.eql({ id: '123', email: 'xyz@example.com' }); + }); + + it('converts to plain object', () => { + const customer = createCustomer(); + customer.unknown = 'abc'; + expect(customer.toObject()).to.eql({ id: '123', email: 'xyz@example.com' }); + expect(customer.toObject({ignoreUnknownProperties: false})) + .to.eql({ id: '123', email: 'xyz@example.com', unknown: 'abc' }); + }); + + it('gets id', () => { + const customer = createCustomer(); + expect(customer.getId()).to.eql('123'); + }); + + it('gets id object', () => { + const customer = createCustomer(); + expect(customer.getIdObject()).to.eql({id: '123'}); + }); + + it('builds where for id', () => { + const where = Customer.buildWhereForId('123'); + expect(where).to.eql({id: '123'}); + }); + + it('gets composite id', () => { + const customer = createRealmCustomer(); + expect(customer.getId()).to.eql( + {realm: 'org1', email: 'xyz@example.com'}); + }); + + it('gets composite id object', () => { + const customer = createRealmCustomer(); + expect(customer.getIdObject()).to.eql( + {realm: 'org1', email: 'xyz@example.com'}); + }); + + it('builds where for composite id', () => { + const where = RealmCustomer.buildWhereForId( + {realm: 'org1', email: 'xyz@example.com'}); + expect(where).to.eql({realm: 'org1', email: 'xyz@example.com'}); + }); + });