From d22962deed8ed51f48f89b7d693b86bf2b19d930 Mon Sep 17 00:00:00 2001 From: Agnes Lin Date: Thu, 19 Sep 2019 22:10:16 -0400 Subject: [PATCH] fix: fixup --- .../belongs-to.relation.acceptance.ts | 1 + ...-inclusion-resolver.relation.acceptance.ts | 409 ++++++++---------- .../has-many.relation.acceptance.ts | 1 + .../acceptance/has-one.relation.acceptance.ts | 1 + .../src/crud/relations/helpers.ts | 27 +- 5 files changed, 210 insertions(+), 229 deletions(-) diff --git a/packages/repository-tests/src/crud/relations/acceptance/belongs-to.relation.acceptance.ts b/packages/repository-tests/src/crud/relations/acceptance/belongs-to.relation.acceptance.ts index 995e9a65c8c1..bfa4f75a41eb 100644 --- a/packages/repository-tests/src/crud/relations/acceptance/belongs-to.relation.acceptance.ts +++ b/packages/repository-tests/src/crud/relations/acceptance/belongs-to.relation.acceptance.ts @@ -52,6 +52,7 @@ export function belongsToRelationAcceptance( ({customerRepo, orderRepo, shipmentRepo} = givenBoundCrudRepositories( ctx.dataSource, repositoryClass, + features, )); const models = [Customer, Order, Shipment]; await ctx.dataSource.automigrate(models.map(m => m.name)); diff --git a/packages/repository-tests/src/crud/relations/acceptance/has-many-inclusion-resolver.relation.acceptance.ts b/packages/repository-tests/src/crud/relations/acceptance/has-many-inclusion-resolver.relation.acceptance.ts index 6556e7bc8090..c38eeb5fda09 100644 --- a/packages/repository-tests/src/crud/relations/acceptance/has-many-inclusion-resolver.relation.acceptance.ts +++ b/packages/repository-tests/src/crud/relations/acceptance/has-many-inclusion-resolver.relation.acceptance.ts @@ -32,242 +32,195 @@ export function hasManyRelationAcceptance( skipIf<[(this: Suite) => void], void>( !features.supportsInclusionResolvers, describe, - 'retrieve models including relations', - () => { - describe('HasMany inclusion resolvers - acceptance', () => { - before(deleteAllModelsInDefaultDataSource); - let customerRepo: CustomerRepository; - let orderRepo: OrderRepository; - let existingCustomerId: MixedIdType; - - before( - withCrudCtx(async function setupRepository(ctx: CrudTestContext) { - // when running the test suite on MongoDB, we don't really need to setup - // this config for mongo connector to pass the test. - // however real-world applications might have such config for MongoDB - // setting it up to check if it works fine as well - Order.definition.properties.customerId.type = features.idType; - Order.definition.properties.customerId.mongodb = { - dataType: 'ObjectID', - }; - // this helper should create the inclusion resolvers for us - ({customerRepo, orderRepo} = givenBoundCrudRepositories( - ctx.dataSource, - repositoryClass, - )); - // inclusionResolvers should be defined. And resolver for each - // relation should be created by the hasManyFactory at this point. - expect(customerRepo.inclusionResolvers).to.not.be.undefined(); - expect(orderRepo.inclusionResolvers).to.not.be.undefined(); - expect(customerRepo.orders.inclusionResolver).to.not.be.undefined(); - expect( - customerRepo.customers.inclusionResolver, - ).to.not.be.undefined(); - // inclusionResolvers shouldn't setup yet at this point - expect(customerRepo.inclusionResolvers).to.deepEqual(new Map()); - - await ctx.dataSource.automigrate([Customer.name, Order.name]); - }), - ); - - beforeEach(async () => { - customerRepo.inclusionResolvers.set( - 'orders', - customerRepo.orders.inclusionResolver, - ); - customerRepo.inclusionResolvers.set( - 'customers', - customerRepo.customers.inclusionResolver, - ); - await customerRepo.deleteAll(); - await orderRepo.deleteAll(); - }); - - it("defines a repository's inclusionResolvers property", () => { - expect(customerRepo.inclusionResolvers).to.not.be.undefined(); - expect(orderRepo.inclusionResolvers).to.not.be.undefined(); - }); - - it("throws an error if the repository doesn't have such relation names", async () => { - await orderRepo.create({ - customerId: existingCustomerId, - description: 'Order from Order McForder, the hoarder of Mordor', - }); - await expect( - customerRepo.find({include: [{relation: 'managers'}]}), - ).to.be.rejectedWith( - `Invalid "filter.include" entries: {"relation":"managers"}`, - ); - }); - - it('throws error if the target repository does not have the registered resolver', async () => { - await orderRepo.create({ - customerId: existingCustomerId, - description: 'Order from Order McForder, the hoarder of Mordor', - }); - // unregister the resolver - customerRepo.inclusionResolvers.delete('orders'); - - await expect( - customerRepo.find({include: [{relation: 'orders'}]}), - ).to.be.rejectedWith( - `Invalid "filter.include" entries: {"relation":"orders"}`, - ); - // reset - customerRepo.inclusionResolvers.set( - 'orders', - customerRepo.orders.inclusionResolver, - ); - }); - - it('simple has-many relation retrieve via find() method', async () => { - const c1 = await customerRepo.create({name: 'c1'}); - const o1 = await orderRepo.create({ - customerId: c1.id, - description: 'order from c1', - }); - const result = await customerRepo.find({ - include: [{relation: 'orders'}], - }); - - const expected = { - id: c1.id, - name: 'c1', - orders: [ - { - id: o1.id, - description: 'order from c1', - customerId: c1.id, - isShipped: features.emptyValue, - // eslint-disable-next-line @typescript-eslint/camelcase - shipment_id: features.emptyValue, - }, - ], - parentId: features.emptyValue, - }; - expect(toJSON(result)).to.deepEqual([toJSON(expected)]); - }); + 'HasMany inclusion resolvers - acceptance', + suite, + ); + function suite() { + before(deleteAllModelsInDefaultDataSource); + let customerRepo: CustomerRepository; + let orderRepo: OrderRepository; + let existingCustomerId: MixedIdType; + + before( + withCrudCtx(async function setupRepository(ctx: CrudTestContext) { + // this helper should create the inclusion resolvers and also + // register inclusion resolvers for us + ({customerRepo, orderRepo} = givenBoundCrudRepositories( + ctx.dataSource, + repositoryClass, + features, + )); + expect(customerRepo.orders.inclusionResolver).to.be.Function(); + + await ctx.dataSource.automigrate([Customer.name, Order.name]); + }), + ); + + beforeEach(async () => { + await customerRepo.deleteAll(); + await orderRepo.deleteAll(); + }); + + it("throws an error if the repository doesn't have such relation names", async () => { + await orderRepo.create({ + customerId: existingCustomerId, + description: 'Order from Order McForder, the hoarder of Mordor', + }); + await expect( + customerRepo.find({include: [{relation: 'managers'}]}), + ).to.be.rejectedWith( + `Invalid "filter.include" entries: {"relation":"managers"}`, + ); + }); + + it('returns single model instance including single related instance', async () => { + const thor = await customerRepo.create({name: 'Thor'}); + const thorOrder = await orderRepo.create({ + customerId: thor.id, + description: "Thor's Mjolnir", + }); + const result = await customerRepo.find({ + include: [{relation: 'orders'}], + }); - it('returns related instances to target models via find() method', async () => { - const c1 = await customerRepo.create({name: 'Thor'}); - const c2 = await customerRepo.create({name: 'Hella'}); - const o1 = await orderRepo.create({ - customerId: c1.id, - description: 'Mjolnir', - }); - const o2 = await orderRepo.create({ - customerId: c1.id, - description: 'Pizza', - }); - const o3 = await orderRepo.create({ - customerId: c2.id, - description: 'Blade', - }); + expect(toJSON(result)).to.deepEqual([ + toJSON({ + ...thor, + parentId: features.emptyValue, + orders: [ + { + ...thorOrder, + isShipped: features.emptyValue, + // eslint-disable-next-line @typescript-eslint/camelcase + shipment_id: features.emptyValue, + }, + ], + }), + ]); + }); + + it('returns multiple model instances including related instances', async () => { + const thor = await customerRepo.create({name: 'Thor'}); + const odin = await customerRepo.create({name: 'Odin'}); + const thorOrderMjolnir = await orderRepo.create({ + customerId: thor.id, + description: 'Mjolnir', + }); + const thorOrderPizza = await orderRepo.create({ + customerId: thor.id, + description: 'Pizza', + }); + const odinOrderCoffee = await orderRepo.create({ + customerId: odin.id, + description: 'Coffee', + }); - const result = await customerRepo.find({ - include: [{relation: 'orders'}], - }); + const result = await customerRepo.find({ + include: [{relation: 'orders'}], + }); - const expected = [ + const expected = [ + { + ...thor, + orders: [ { - id: c1.id, - name: 'Thor', - orders: [ - { - id: o1.id, - description: 'Mjolnir', - customerId: c1.id, - isShipped: features.emptyValue, - // eslint-disable-next-line @typescript-eslint/camelcase - shipment_id: features.emptyValue, - }, - { - id: o2.id, - description: 'Pizza', - customerId: c1.id, - isShipped: features.emptyValue, - // eslint-disable-next-line @typescript-eslint/camelcase - shipment_id: features.emptyValue, - }, - ], - parentId: features.emptyValue, + ...thorOrderMjolnir, + isShipped: features.emptyValue, + // eslint-disable-next-line @typescript-eslint/camelcase + shipment_id: features.emptyValue, }, { - id: c2.id, - name: 'Hella', - orders: [ - { - id: o3.id, - description: 'Blade', - customerId: c2.id, - isShipped: features.emptyValue, - // eslint-disable-next-line @typescript-eslint/camelcase - shipment_id: features.emptyValue, - }, - ], - parentId: features.emptyValue, + ...thorOrderPizza, + isShipped: features.emptyValue, + // eslint-disable-next-line @typescript-eslint/camelcase + shipment_id: features.emptyValue, }, - ]; - expect(toJSON(result)).to.deepEqual(toJSON(expected)); - }); - - it('returns related instances to target models via findById() method', async () => { - const c1 = await customerRepo.create({name: 'Thor'}); - const c2 = await customerRepo.create({name: 'Hella'}); - await orderRepo.create({ - customerId: c1.id, - description: 'Mjolnir', - }); - await orderRepo.create({ - customerId: c1.id, - description: 'Pizza', - }); - const o3 = await orderRepo.create({ - customerId: c2.id, - description: 'Blade', - }); - - const result = await customerRepo.findById(c2.id, { - include: [{relation: 'orders'}], - }); - const expected = { - id: c2.id, - name: 'Hella', - orders: [ - { - id: o3.id, - description: 'Blade', - customerId: c2.id, - isShipped: features.emptyValue, - // eslint-disable-next-line @typescript-eslint/camelcase - shipment_id: features.emptyValue, - }, - ], - parentId: features.emptyValue, - }; - expect(toJSON(result)).to.deepEqual(toJSON(expected)); - }); - - it('throws when navigational properties are present when updating model instance', async () => { - const created = await customerRepo.create({name: 'c1'}); - const customerId = created.id; - - await orderRepo.create({ - description: 'Pen', - customerId, - }); + ], + parentId: features.emptyValue, + }, + { + ...odin, + parentId: features.emptyValue, + orders: [ + { + ...odinOrderCoffee, + isShipped: features.emptyValue, + // eslint-disable-next-line @typescript-eslint/camelcase + shipment_id: features.emptyValue, + }, + ], + }, + ]; + expect(toJSON(result)).to.deepEqual(toJSON(expected)); + }); + + it('returns a specified instance including its related model instances', async () => { + const thor = await customerRepo.create({name: 'Thor'}); + const odin = await customerRepo.create({name: 'Odin'}); + await orderRepo.create({ + customerId: thor.id, + description: 'Mjolnir', + }); + await orderRepo.create({ + customerId: thor.id, + description: 'Pizza', + }); + const odinOrder = await orderRepo.create({ + customerId: odin.id, + description: 'Coffee', + }); - const found = await customerRepo.findById(customerId, { - include: [{relation: 'orders'}], - }); - expect(found.orders).to.have.lengthOf(1); + const result = await customerRepo.findById(odin.id, { + include: [{relation: 'orders'}], + }); + const expected = { + ...odin, + parentId: features.emptyValue, + orders: [ + { + ...odinOrder, + isShipped: features.emptyValue, + // eslint-disable-next-line @typescript-eslint/camelcase + shipment_id: features.emptyValue, + }, + ], + }; + expect(toJSON(result)).to.deepEqual(toJSON(expected)); + }); + + it('throws when navigational properties are present when updating model instance', async () => { + const created = await customerRepo.create({name: 'customer'}); + const customerId = created.id; + + await orderRepo.create({ + description: 'pizza', + customerId, + }); - found.name = 'updated name'; - await expect(customerRepo.save(found)).to.be.rejectedWith( - 'The `Customer` instance is not valid. Details: `orders` is not defined in the model (value: undefined).', - ); - }); + const found = await customerRepo.findById(customerId, { + include: [{relation: 'orders'}], }); - }, - ); + expect(found.orders).to.have.lengthOf(1); + + found.name = 'updated name'; + await expect(customerRepo.save(found)).to.be.rejectedWith( + 'The `Customer` instance is not valid. Details: `orders` is not defined in the model (value: undefined).', + ); + }); + + it('throws error if the target repository does not have the registered resolver', async () => { + await orderRepo.create({ + customerId: existingCustomerId, + description: 'Order from Order McForder, the hoarder of Mordor', + }); + // unregister the resolver + customerRepo.inclusionResolvers.delete('orders'); + + await expect( + customerRepo.find({include: [{relation: 'orders'}]}), + ).to.be.rejectedWith( + `Invalid "filter.include" entries: {"relation":"orders"}`, + ); + }); + } } diff --git a/packages/repository-tests/src/crud/relations/acceptance/has-many.relation.acceptance.ts b/packages/repository-tests/src/crud/relations/acceptance/has-many.relation.acceptance.ts index 905aaacc57de..106cc3864049 100644 --- a/packages/repository-tests/src/crud/relations/acceptance/has-many.relation.acceptance.ts +++ b/packages/repository-tests/src/crud/relations/acceptance/has-many.relation.acceptance.ts @@ -40,6 +40,7 @@ export function hasManyRelationAcceptance( ({customerRepo, orderRepo} = givenBoundCrudRepositories( ctx.dataSource, repositoryClass, + features, )); await ctx.dataSource.automigrate([Customer.name, Order.name]); }), diff --git a/packages/repository-tests/src/crud/relations/acceptance/has-one.relation.acceptance.ts b/packages/repository-tests/src/crud/relations/acceptance/has-one.relation.acceptance.ts index a8b791a9573d..acd97bf1de6f 100644 --- a/packages/repository-tests/src/crud/relations/acceptance/has-one.relation.acceptance.ts +++ b/packages/repository-tests/src/crud/relations/acceptance/has-one.relation.acceptance.ts @@ -41,6 +41,7 @@ export function hasOneRelationAcceptance( ({customerRepo, addressRepo} = givenBoundCrudRepositories( ctx.dataSource, repositoryClass, + features, )); const models = [Customer, Address]; await ctx.dataSource.automigrate(models.map(m => m.name)); diff --git a/packages/repository-tests/src/crud/relations/helpers.ts b/packages/repository-tests/src/crud/relations/helpers.ts index 79a989b5fc39..1aff42db90a1 100644 --- a/packages/repository-tests/src/crud/relations/helpers.ts +++ b/packages/repository-tests/src/crud/relations/helpers.ts @@ -4,10 +4,12 @@ // License text available at https://opensource.org/licenses/MIT import {juggler} from '@loopback/repository'; -import {CrudRepositoryCtor} from '../..'; +import {CrudFeatures, CrudRepositoryCtor} from '../..'; import { + Address, AddressRepository, CustomerRepository, + Order, OrderRepository, ShipmentRepository, } from './fixtures/models'; @@ -21,7 +23,20 @@ import { export function givenBoundCrudRepositories( db: juggler.DataSource, repositoryClass: CrudRepositoryCtor, + features: CrudFeatures, ) { + // when running the test suite on MongoDB, we don't really need to setup + // this config for mongo connector to pass the test. + // however real-world applications might have such config for MongoDB + // setting it up to check if it works fine as well + Order.definition.properties.customerId.type = features.idType; + Order.definition.properties.customerId.mongodb = { + dataType: 'ObjectID', + }; + Address.definition.properties.customerId.type = features.idType; + Address.definition.properties.customerId.mongodb = { + dataType: 'ObjectID', + }; // get the repository class and create a new instance of it const customerRepoClass = createCustomerRepo(repositoryClass); const customerRepo: CustomerRepository = new customerRepoClass( @@ -29,6 +44,16 @@ export function givenBoundCrudRepositories( async () => orderRepo, async () => addressRepo, ); + // register the inclusionResolvers here for customerRepo + + customerRepo.inclusionResolvers.set( + 'orders', + customerRepo.orders.inclusionResolver, + ); + customerRepo.inclusionResolvers.set( + 'customers', + customerRepo.customers.inclusionResolver, + ); const orderRepoClass = createOrderRepo(repositoryClass); const orderRepo: OrderRepository = new orderRepoClass(