diff --git a/examples/todo-list/src/controllers/author.controller.ts b/examples/todo-list/src/controllers/author.controller.ts deleted file mode 100644 index 9c2de3de8a89..000000000000 --- a/examples/todo-list/src/controllers/author.controller.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { - Count, - CountSchema, - Filter, - repository, - Where, -} from '@loopback/repository'; -import { - post, - param, - get, - getFilterSchemaFor, - getWhereSchemaFor, - patch, - del, - requestBody, -} from '@loopback/rest'; -import {Author} from '../models'; -import {AuthorRepository} from '../repositories'; - -export class AuthorController { - constructor( - @repository(AuthorRepository) - public authorRepository : AuthorRepository, - ) {} - - @post('/authors', { - responses: { - '200': { - description: 'Author model instance', - content: {'application/json': {schema: {'x-ts-type': Author}}}, - }, - }, - }) - async create(@requestBody() author: Author): Promise { - return await this.authorRepository.create(author); - } - - @get('/authors/count', { - responses: { - '200': { - description: 'Author model count', - content: {'application/json': {schema: CountSchema}}, - }, - }, - }) - async count( - @param.query.object('where', getWhereSchemaFor(Author)) where?: Where, - ): Promise { - return await this.authorRepository.count(where); - } - - @get('/authors', { - responses: { - '200': { - description: 'Array of Author model instances', - content: { - 'application/json': { - schema: {type: 'array', items: {'x-ts-type': Author}}, - }, - }, - }, - }, - }) - async find( - @param.query.object('filter', getFilterSchemaFor(Author)) filter?: Filter, - ): Promise { - return await this.authorRepository.find(filter); - } - - @patch('/authors', { - responses: { - '200': { - description: 'Author PATCH success count', - content: {'application/json': {schema: CountSchema}}, - }, - }, - }) - async updateAll( - @requestBody() author: Author, - @param.query.object('where', getWhereSchemaFor(Author)) where?: Where, - ): Promise { - return await this.authorRepository.updateAll(author, where); - } - - @get('/authors/{id}', { - responses: { - '200': { - description: 'Author model instance', - content: {'application/json': {schema: {'x-ts-type': Author}}}, - }, - }, - }) - async findById(@param.path.string('id') id: string): Promise { - return await this.authorRepository.findById(id); - } - - @patch('/authors/{id}', { - responses: { - '204': { - description: 'Author PATCH success', - }, - }, - }) - async updateById( - @param.path.string('id') id: string, - @requestBody() author: Author, - ): Promise { - await this.authorRepository.updateById(id, author); - } - - @del('/authors/{id}', { - responses: { - '204': { - description: 'Author DELETE success', - }, - }, - }) - async deleteById(@param.path.string('id') id: string): Promise { - await this.authorRepository.deleteById(id); - } -} diff --git a/examples/todo-list/src/controllers/index.ts b/examples/todo-list/src/controllers/index.ts index 8a42be51d2f0..75adca1e30bc 100644 --- a/examples/todo-list/src/controllers/index.ts +++ b/examples/todo-list/src/controllers/index.ts @@ -6,5 +6,4 @@ export * from './todo.controller'; export * from './todo-list.controller'; export * from './todo-list-todo.controller'; -export * from './todo-list-author.controller'; -export * from './author.controller'; +export * from './todo-list-image.controller'; diff --git a/examples/todo-list/src/controllers/todo-list-author.controller.ts b/examples/todo-list/src/controllers/todo-list-author.controller.ts deleted file mode 100644 index 9ae48125248e..000000000000 --- a/examples/todo-list/src/controllers/todo-list-author.controller.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {TodoListRepository} from '../repositories'; -import {repository, Filter} from '@loopback/repository'; -import {param, post, requestBody, get} from '@loopback/rest'; -import {Author} from '../models'; - -export class TodoListAuthorController { - constructor( - @repository(TodoListRepository) protected todoListRepo: TodoListRepository, - ) {} - - @post('/todo-lists/{id}/author', { - responses: { - '200': { - description: 'create Author model instance', - content: {'application/json': {schema: {'x-ts-type': Author}}}, - }, - }, - }) - async create( - @param.path.number('id') id: number, - @requestBody() author: Author, - ): Promise { - return await this.todoListRepo.author(id).create(author); - } - - @get('/todo-lists/{id}/author', { - responses: { - '200': { - description: 'The author belonging to the TodoList', - content: {'application/json': {schema: {'x-ts-type': Author}}}, - }, - }, - }) - async find(@param.path.number('id') id: number): Promise { - return await this.todoListRepo.author(id).get(); - } -} diff --git a/examples/todo-list/src/controllers/todo-list-image.controller.ts b/examples/todo-list/src/controllers/todo-list-image.controller.ts new file mode 100644 index 000000000000..d909f4b156bc --- /dev/null +++ b/examples/todo-list/src/controllers/todo-list-image.controller.ts @@ -0,0 +1,37 @@ +import {TodoListRepository} from '../repositories'; +import {repository} from '@loopback/repository'; +import {param, post, requestBody, get} from '@loopback/rest'; +import {TodoListImage} from '../models'; + +export class TodoListImageController { + constructor( + @repository(TodoListRepository) protected todoListRepo: TodoListRepository, + ) {} + + @post('/todo-lists/{id}/image', { + responses: { + '200': { + description: 'create todoListImage model instance', + content: {'application/json': {schema: {'x-ts-type': TodoListImage}}}, + }, + }, + }) + async create( + @param.path.number('id') id: number, + @requestBody() image: TodoListImage, + ): Promise { + return await this.todoListRepo.image(id).create(image); + } + + @get('/todo-lists/{id}/image', { + responses: { + '200': { + description: 'The image belonging to the TodoList', + content: {'application/json': {schema: {'x-ts-type': TodoListImage}}}, + }, + }, + }) + async find(@param.path.number('id') id: number): Promise { + return await this.todoListRepo.image(id).get(); + } +} diff --git a/examples/todo-list/src/models/author.model.ts b/examples/todo-list/src/models/author.model.ts deleted file mode 100644 index 61e0b3754c6f..000000000000 --- a/examples/todo-list/src/models/author.model.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {Entity, model, property, belongsTo} from '@loopback/repository'; -import {TodoList} from './todo-list.model'; - -@model() -export class Author extends Entity { - @belongsTo( - () => TodoList, - {}, - { - id: true, - generated: false, - type: 'string', - }, - ) - todoListId: string; - - @property({ - type: 'string', - required: true, - }) - name: string; - - constructor(data?: Partial) { - super(data); - } -} diff --git a/examples/todo-list/src/models/index.ts b/examples/todo-list/src/models/index.ts index 0fa038ca8ce0..63f4e48dfe36 100644 --- a/examples/todo-list/src/models/index.ts +++ b/examples/todo-list/src/models/index.ts @@ -5,4 +5,4 @@ export * from './todo.model'; export * from './todo-list.model'; -export * from './author.model'; +export * from './todo-list-image.model'; diff --git a/examples/todo-list/src/models/todo-list-image.model.ts b/examples/todo-list/src/models/todo-list-image.model.ts new file mode 100644 index 000000000000..176178b05b2f --- /dev/null +++ b/examples/todo-list/src/models/todo-list-image.model.ts @@ -0,0 +1,20 @@ +import {Entity, model, property, belongsToUniquely} from '@loopback/repository'; +import {TodoList} from './todo-list.model'; + +@model() +export class TodoListImage extends Entity { + @belongsToUniquely(() => TodoList) + todoListId?: number; + + @property({ + required: true, + }) + // Ideally we would use Buffer type here, but + // that is not supported yet. + // see https://github.com/strongloop/loopback-next/issues/1742 + value: string; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/examples/todo-list/src/models/todo-list.model.ts b/examples/todo-list/src/models/todo-list.model.ts index dff46d0c226f..d555cd381e24 100644 --- a/examples/todo-list/src/models/todo-list.model.ts +++ b/examples/todo-list/src/models/todo-list.model.ts @@ -5,7 +5,7 @@ import {Entity, model, property, hasMany, hasOne} from '@loopback/repository'; import {Todo} from './todo.model'; -import {Author} from './author.model'; +import {TodoListImage} from './todo-list-image.model'; @model() export class TodoList extends Entity { @@ -29,8 +29,8 @@ export class TodoList extends Entity { @hasMany(() => Todo) todos: Todo[]; - @hasOne(() => Author) - author: Author; + @hasOne(() => TodoListImage) + image: TodoListImage; constructor(data?: Partial) { super(data); diff --git a/examples/todo-list/src/repositories/index.ts b/examples/todo-list/src/repositories/index.ts index de4989b06701..ca53ed66422d 100644 --- a/examples/todo-list/src/repositories/index.ts +++ b/examples/todo-list/src/repositories/index.ts @@ -5,4 +5,3 @@ export * from './todo.repository'; export * from './todo-list.repository'; -export * from './author.repository'; diff --git a/examples/todo-list/src/repositories/author.repository.ts b/examples/todo-list/src/repositories/todo-list-image.repository.ts similarity index 70% rename from examples/todo-list/src/repositories/author.repository.ts rename to examples/todo-list/src/repositories/todo-list-image.repository.ts index ead7c3302844..a776f6e9b98b 100644 --- a/examples/todo-list/src/repositories/author.repository.ts +++ b/examples/todo-list/src/repositories/todo-list-image.repository.ts @@ -1,28 +1,27 @@ import { DefaultCrudRepository, - juggler, repository, BelongsToAccessor, } from '@loopback/repository'; -import {Author, TodoList} from '../models'; +import {TodoListImage, TodoList} from '../models'; import {DbDataSource} from '../datasources'; import {inject, Getter} from '@loopback/core'; import {TodoListRepository} from './todo-list.repository'; -export class AuthorRepository extends DefaultCrudRepository< - Author, - typeof Author.prototype.todoListId +export class TodoListImageRepository extends DefaultCrudRepository< + TodoListImage, + typeof TodoListImage.prototype.todoListId > { public readonly todoList: BelongsToAccessor< TodoList, - typeof Author.prototype.todoListId + typeof TodoListImage.prototype.todoListId >; constructor( @inject('datasources.db') dataSource: DbDataSource, @repository.getter('TodoListRepository') protected todoListRepositoryGetter: Getter, ) { - super(Author, dataSource); + super(TodoListImage, dataSource); this.todoList = this._createBelongsToAccessorFor( 'todoList', todoListRepositoryGetter, diff --git a/examples/todo-list/src/repositories/todo-list.repository.ts b/examples/todo-list/src/repositories/todo-list.repository.ts index d1c32cdeccc0..8ad2b24221dd 100644 --- a/examples/todo-list/src/repositories/todo-list.repository.ts +++ b/examples/todo-list/src/repositories/todo-list.repository.ts @@ -11,9 +11,9 @@ import { repository, HasOneRepositoryFactory, } from '@loopback/repository'; -import {Todo, TodoList, Author} from '../models'; +import {Todo, TodoList, TodoListImage} from '../models'; import {TodoRepository} from './todo.repository'; -import {AuthorRepository} from './author.repository'; +import {TodoListImageRepository} from './todo-list-image.repository'; export class TodoListRepository extends DefaultCrudRepository< TodoList, @@ -23,8 +23,8 @@ export class TodoListRepository extends DefaultCrudRepository< Todo, typeof TodoList.prototype.id >; - public readonly author: HasOneRepositoryFactory< - Author, + public readonly image: HasOneRepositoryFactory< + TodoListImage, typeof TodoList.prototype.id >; @@ -32,16 +32,16 @@ export class TodoListRepository extends DefaultCrudRepository< @inject('datasources.db') dataSource: juggler.DataSource, @repository.getter('TodoRepository') protected todoRepositoryGetter: Getter, - @repository.getter('AuthorRepository') - protected todoListImageRepositoryGetter: Getter, + @repository.getter('TodoListImageRepository') + protected todoListImageRepositoryGetter: Getter, ) { super(TodoList, dataSource); this.todos = this._createHasManyRepositoryFactoryFor( 'todos', todoRepositoryGetter, ); - this.author = this._createHasOneRepositoryFactoryFor( - 'author', + this.image = this._createHasOneRepositoryFactoryFor( + 'image', todoListImageRepositoryGetter, ); } diff --git a/packages/repository/src/relations/belongs-to/belongs-to.decorator.ts b/packages/repository/src/relations/belongs-to/belongs-to.decorator.ts index e346603b8d53..6776b2b4cd79 100644 --- a/packages/repository/src/relations/belongs-to/belongs-to.decorator.ts +++ b/packages/repository/src/relations/belongs-to/belongs-to.decorator.ts @@ -11,8 +11,11 @@ import {BelongsToDefinition, RelationType} from '../relation.types'; /** * Decorator for belongsTo - * @param targetResolver - * @param definition + * @param targetResolver A resolver function that returns the target model for + * a belongsTo relation + * @param definition Optional metadata for setting up a belongsTo relation + * @param propertyMeta Optional property metadata to call the proprety decorator + * with * @returns {(target: Object, key:string)} */ export function belongsTo( @@ -55,3 +58,22 @@ export function belongsTo( relation(meta)(decoratedTarget, decoratedKey); }; } + +/** + * Sugar syntax for belongsTo decorator for hasOne relation. Calls belongsTo + * with property definition defaults for non-generated id field + * @param targetResolver A resolver function that returns the target model for + * a belongsTo relation + * @param definition Optional metadata for setting up a belongsTo relation + * @returns {(target: Object, key:string)} + */ +export function belongsToUniquely( + targetResolver: EntityResolver, + definition?: Partial, +) { + const propertyMetaDefaults: Partial = { + id: true, + generated: false, + }; + return belongsTo(targetResolver, definition, propertyMetaDefaults); +} diff --git a/packages/repository/src/relations/has-many/has-many-repository.factory.ts b/packages/repository/src/relations/has-many/has-many-repository.factory.ts index 6f6b942fd480..626766f4b7a6 100644 --- a/packages/repository/src/relations/has-many/has-many-repository.factory.ts +++ b/packages/repository/src/relations/has-many/has-many-repository.factory.ts @@ -28,9 +28,9 @@ export type HasManyRepositoryFactory = ( * via a HasMany relation, then, the relational repository returned by the * factory function would be constrained by a Customer model instance's id(s). * - * @param relationMeta The relation metadata used to describe the + * @param relationMetadata The relation metadata used to describe the * relationship and determine how to apply the constraint. - * @param targetRepo The repository which represents the target model of a + * @param targetRepositoryGetter The repository which represents the target model of a * relation attached to a datasource. * @returns The factory function which accepts a foreign key value to constrain * the given target repository diff --git a/packages/repository/src/relations/has-one/has-one-repository.factory.ts b/packages/repository/src/relations/has-one/has-one-repository.factory.ts index e052a4762f74..3e9c5a73182a 100644 --- a/packages/repository/src/relations/has-one/has-one-repository.factory.ts +++ b/packages/repository/src/relations/has-one/has-one-repository.factory.ts @@ -25,9 +25,9 @@ export type HasOneRepositoryFactory = ( * via a HasOne relation, then, the relational repository returned by the * factory function would be constrained by a Customer model instance's id(s). * - * @param relationMeta The relation metadata used to describe the + * @param relationMetadata The relation metadata used to describe the * relationship and determine how to apply the constraint. - * @param targetRepo The repository which represents the target model of a + * @param targetRepositoryGetter The repository which represents the target model of a * relation attached to a datasource. * @returns The factory function which accepts a foreign key value to constrain * the given target repository @@ -56,10 +56,10 @@ export function createHasOneRepositoryFactory< type HasOneResolvedDefinition = HasOneDefinition & {keyTo: string}; /** - * Resolves given hasMany metadata if target is specified to be a resolver. + * Resolves given hasOne metadata if target is specified to be a resolver. * Mainly used to infer what the `keyTo` property should be from the target's - * belongsTo metadata - * @param relationMeta hasMany metadata to resolve + * hasOne metadata + * @param relationMeta hasOne metadata to resolve */ function resolveHasOneMetadata( relationMeta: HasOneDefinition, diff --git a/packages/repository/src/relations/has-one/has-one.repository.ts b/packages/repository/src/relations/has-one/has-one.repository.ts index cd3b37012e4b..8e4e9e1356ba 100644 --- a/packages/repository/src/relations/has-one/has-one.repository.ts +++ b/packages/repository/src/relations/has-one/has-one.repository.ts @@ -6,12 +6,12 @@ import {Getter} from '@loopback/context'; import {DataObject, Options} from '../../common-types'; import {Entity} from '../../model'; -import {Filter, Where} from '../../query'; +import {Filter} from '../../query'; import { constrainDataObject, constrainFilter, -} from '../../repositories/constraint-utils'; -import {EntityCrudRepository} from '../../repositories/repository'; + EntityCrudRepository, +} from '../../repositories'; import {EntityNotFoundError} from '../../errors'; /** @@ -36,9 +36,9 @@ export interface HasOneRepository { * @returns A promise of the target object or null if not found. */ get( - filter?: Exclude, Where>, + filter?: Pick, Exclude, 'where'>>, options?: Options, - ): Promise; + ): Promise; } export class DefaultHasOneRepository< @@ -69,7 +69,10 @@ export class DefaultHasOneRepository< } async get( - filter?: Exclude, Where>, + filter?: Pick< + Filter, + Exclude, 'where'> + >, options?: Options, ): Promise { const targetRepository = await this.getTargetRepository(); diff --git a/packages/repository/test/acceptance/has-one.relation.acceptance.ts b/packages/repository/test/acceptance/has-one.relation.acceptance.ts index 1d8011814b6e..d781935be891 100644 --- a/packages/repository/test/acceptance/has-one.relation.acceptance.ts +++ b/packages/repository/test/acceptance/has-one.relation.acceptance.ts @@ -4,7 +4,7 @@ // License text available at https://opensource.org/licenses/MIT import {Application} from '@loopback/core'; -import {expect} from '@loopback/testlab'; +import {expect, toJSON} from '@loopback/testlab'; import * as _ from 'lodash'; import { ApplicationWithRepositories, @@ -12,10 +12,10 @@ import { repository, RepositoryMixin, Filter, + EntityNotFoundError, } from '../..'; import {Address} from '../fixtures/models'; import {CustomerRepository, AddressRepository} from '../fixtures/repositories'; -import {Where} from '../..'; describe('hasOne relation', () => { // Given a Customer and Address models - see definitions at the bottom @@ -32,9 +32,6 @@ describe('hasOne relation', () => { beforeEach(async () => { await addressRepo.deleteAll(); - }); - - beforeEach(async () => { existingCustomerId = (await givenPersistedCustomerInstance()).id; }); @@ -51,7 +48,7 @@ describe('hasOne relation', () => { expect(persisted.toObject()).to.deepEqual(address.toObject()); }); - it("doesn't allow to create related model instance twice", async () => { + it('refuses to create related model instance twice', async () => { const address = await controller.createCustomerAddress(existingCustomerId, { street: '123 test avenue', }); @@ -73,17 +70,11 @@ describe('hasOne relation', () => { const address = await controller.createCustomerAddress(existingCustomerId, { street: '123 test avenue', }); - const notMyAddress = await controller.createCustomerAddress( - existingCustomerId + 1, - { - street: '456 test road', - }, - ); const foundAddress = await controller.findCustomerAddress( existingCustomerId, ); expect(foundAddress).to.containEql(address); - expect(foundAddress).to.not.containEql(notMyAddress); + expect(toJSON(foundAddress)).to.deepEqual(toJSON(address)); const persisted = await addressRepo.find({ where: {customerId: existingCustomerId}, @@ -91,25 +82,37 @@ describe('hasOne relation', () => { expect(persisted[0]).to.deepEqual(foundAddress); }); - it('does not allow where filter to find related model instance', async () => { - const address = await controller.createCustomerAddress(existingCustomerId, { - street: '123 test avenue', - }); - + // FIXME(b-admike): make sure the test fails with compiler error + it.skip('ignores where filter to find related model instance', async () => { const foundAddress = await controller.findCustomerAddressWithFilter( existingCustomerId, - {where: {street: '123 test avenue'}}, + // the compiler should complain that the where field is + // not accepted in the filter object for the get() method + // if the following line is uncommented + { + where: {street: '456 test road'}, + }, ); - // TODO: make sure this test fails when where condition is supplied - // compiler should have errored out (?) - expect(foundAddress).to.containEql(address); const persisted = await addressRepo.find({ where: {customerId: existingCustomerId}, }); + // TODO: make sure this test fails when where condition is supplied + // compiler should have errored out (?) expect(persisted[0]).to.deepEqual(foundAddress); }); + it('reports EntityNotFound error when related model is deleted', async () => { + const address = await controller.createCustomerAddress(existingCustomerId, { + street: '123 test avenue', + }); + await addressRepo.deleteById(address.customerId); + + await expect( + controller.findCustomerAddress(existingCustomerId), + ).to.be.rejectedWith(EntityNotFoundError); + }); + /*---------------- HELPERS -----------------*/ class CustomerController { diff --git a/packages/repository/test/fixtures/models/address.model.ts b/packages/repository/test/fixtures/models/address.model.ts index 8bdd6a94548d..0a727f5d1262 100644 --- a/packages/repository/test/fixtures/models/address.model.ts +++ b/packages/repository/test/fixtures/models/address.model.ts @@ -3,7 +3,7 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {Entity, model, property, belongsTo} from '../../..'; +import {Entity, model, property, belongsToUniquely} from '../../..'; import {Customer} from './customer.model'; @model() @@ -25,13 +25,6 @@ export class Address extends Entity { }) province: String; - @belongsTo( - () => Customer, - {}, - { - id: true, - generated: false, - }, - ) + @belongsToUniquely(() => Customer) customerId: number; } diff --git a/packages/repository/test/fixtures/repositories/customer.repository.ts b/packages/repository/test/fixtures/repositories/customer.repository.ts index 35ce49901f82..bd19f9e7b213 100644 --- a/packages/repository/test/fixtures/repositories/customer.repository.ts +++ b/packages/repository/test/fixtures/repositories/customer.repository.ts @@ -12,7 +12,7 @@ import { } from '../../..'; import {Customer, Order, Address} from '../models'; import {OrderRepository} from './order.repository'; -import {HasOneRepositoryFactory} from '../../../src'; +import {HasOneRepositoryFactory} from '../../../'; import {AddressRepository} from './address.repository'; export class CustomerRepository extends DefaultCrudRepository< diff --git a/packages/repository/test/unit/decorator/model-and-relation.decorator.unit.ts b/packages/repository/test/unit/decorator/model-and-relation.decorator.unit.ts index b9bafc71469a..03959ba815a6 100644 --- a/packages/repository/test/unit/decorator/model-and-relation.decorator.unit.ts +++ b/packages/repository/test/unit/decorator/model-and-relation.decorator.unit.ts @@ -8,6 +8,7 @@ import {expect} from '@loopback/testlab'; import {RelationMetadata} from '../../..'; import { belongsTo, + belongsToUniquely, embedsMany, embedsOne, Entity, @@ -97,8 +98,8 @@ describe('model decorator', () => { () => Customer, {}, { - id: false, - generated: true, + id: true, + generated: false, }, ) customerId: string; @@ -108,6 +109,15 @@ describe('model decorator', () => { isShipped: boolean; } + @model() + class RegistrationDate extends Entity { + @belongsToUniquely(() => Customer) + customerId: number; + + @property() + registeredOn: Date; + } + @model() class Customer extends Entity { @property({type: 'string', id: true, generated: true}) @@ -293,6 +303,19 @@ describe('model decorator', () => { expect(relationDef.target()).to.be.exactly(Customer); }); + it('passes property metadata from belongsToUniquely', () => { + const propMeta = + MetadataInspector.getAllPropertyMetadata( + MODEL_PROPERTIES_KEY, + RegistrationDate.prototype, + ) || /* istanbul ignore next */ {}; + + expect(propMeta.customerId).to.containEql({ + id: true, + generated: false, + }); + }); + it('passes property metadata from belongsTo', () => { const propMeta = MetadataInspector.getAllPropertyMetadata( @@ -301,8 +324,8 @@ describe('model decorator', () => { ) || /* istanbul ignore next */ {}; expect(propMeta.customerId).to.containEql({ - id: false, - generated: true, + id: true, + generated: false, }); }); diff --git a/packages/repository/test/unit/repositories/has-one-repository-factory.unit.ts b/packages/repository/test/unit/repositories/has-one-repository-factory.unit.ts index 150cfe033696..6b328888d5fd 100644 --- a/packages/repository/test/unit/repositories/has-one-repository-factory.unit.ts +++ b/packages/repository/test/unit/repositories/has-one-repository-factory.unit.ts @@ -81,7 +81,7 @@ describe('createHasOneRepositoryFactory', () => { it('rejects relations with keyTo that is not an id', () => { const relationMeta = givenHasOneDefinition({ name: 'order', - target: () => Order, + target: () => ModelWithoutIndexFK, }); expect(() => @@ -97,7 +97,7 @@ describe('createHasOneRepositoryFactory', () => { it('rejects relations with keyTo that is a generated id property', () => { const relationMeta = givenHasOneDefinition({ name: 'profile', - target: () => Profile, + target: () => ModelWithGeneratedFK, }); expect(() => @@ -132,8 +132,11 @@ describe('createHasOneRepositoryFactory', () => { province: String; } - class Order extends Entity { - static definition = new ModelDefinition('Order') + // added an id property because the model itself extends from Entity, but the + // purpose here is the decorated relational property is not part of the + // composite index, thus the name ModelWithoutIndexFK. + class ModelWithoutIndexFK extends Entity { + static definition = new ModelDefinition('ModelWithoutIndexFK') .addProperty('customerId', { type: 'string', id: false, @@ -142,18 +145,23 @@ describe('createHasOneRepositoryFactory', () => { type: 'string', required: true, }) + .addProperty('id', { + type: 'number', + id: true, + }) .addProperty('isShipped', { type: 'boolean', required: false, }); + id: number; customerId: string; description: string; isShipped: boolean; } - class Profile extends Entity { - static definition = new ModelDefinition('Profile') + class ModelWithGeneratedFK extends Entity { + static definition = new ModelDefinition('ModelWithGeneratedFK') .addProperty('customerId', { type: 'string', id: true,