-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(repository): implement inclusion resolver for belongsTo relation
Co-authored-by: Miroslav <[email protected]>
- Loading branch information
Showing
15 changed files
with
614 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
183 changes: 183 additions & 0 deletions
183
...-tests/src/crud/relations/acceptance/belongs-to.inclusion-resolver.relation.acceptance.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
// Copyright IBM Corp. 2019. All Rights Reserved. | ||
// Node module: @loopback/repository-tests | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {expect, skipIf, toJSON} from '@loopback/testlab'; | ||
import {Suite} from 'mocha'; | ||
import { | ||
CrudFeatures, | ||
CrudRepositoryCtor, | ||
CrudTestContext, | ||
DataSourceOptions, | ||
} from '../../..'; | ||
import { | ||
deleteAllModelsInDefaultDataSource, | ||
MixedIdType, | ||
withCrudCtx, | ||
} from '../../../helpers.repository-tests'; | ||
import { | ||
Customer, | ||
CustomerRepository, | ||
Order, | ||
OrderRepository, | ||
} from '../fixtures/models'; | ||
import {givenBoundCrudRepositories} from '../helpers'; | ||
|
||
export function belongsToInclusionResolverAcceptance( | ||
dataSourceOptions: DataSourceOptions, | ||
repositoryClass: CrudRepositoryCtor, | ||
features: CrudFeatures, | ||
) { | ||
skipIf<[(this: Suite) => void], void>( | ||
!features.supportsInclusionResolvers, | ||
describe, | ||
'BelongsTo 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(orderRepo.customer.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 it tries to query nonexists relation names', async () => { | ||
await orderRepo.create({ | ||
description: 'shiba', | ||
customerId: existingCustomerId, | ||
}); | ||
await expect( | ||
orderRepo.find({include: [{relation: 'shipment'}]}), | ||
).to.be.rejectedWith( | ||
`Invalid "filter.include" entries: {"relation":"shipment"}`, | ||
); | ||
}); | ||
|
||
it('returns single model instance including single related instance', async () => { | ||
const thor = await customerRepo.create({name: 'Thor'}); | ||
const order = await orderRepo.create({ | ||
description: 'Mjolnir', | ||
customerId: thor.id, | ||
}); | ||
const result = await orderRepo.find({ | ||
include: [{relation: 'customer'}], | ||
}); | ||
|
||
const expected = { | ||
...order, | ||
isShipped: features.emptyValue, | ||
// eslint-disable-next-line @typescript-eslint/camelcase | ||
shipment_id: features.emptyValue, | ||
customer: { | ||
...thor, | ||
parentId: features.emptyValue, | ||
}, | ||
}; | ||
expect(toJSON(result)).to.deepEqual([toJSON(expected)]); | ||
}); | ||
|
||
it('returns multiple model instances including related instances', async () => { | ||
const thor = await customerRepo.create({name: 'Thor'}); | ||
const odin = await customerRepo.create({name: 'Odin'}); | ||
const thorOrder = await orderRepo.create({ | ||
description: "Thor's Mjolnir", | ||
customerId: thor.id, | ||
}); | ||
const odinOrder = await orderRepo.create({ | ||
description: "Odin's Coffee Maker", | ||
customerId: odin.id, | ||
}); | ||
|
||
const result = await orderRepo.find({ | ||
include: [{relation: 'customer'}], | ||
}); | ||
|
||
const expected = [ | ||
{ | ||
...thorOrder, | ||
isShipped: features.emptyValue, | ||
// eslint-disable-next-line @typescript-eslint/camelcase | ||
shipment_id: features.emptyValue, | ||
customer: { | ||
...thor, | ||
parentId: features.emptyValue, | ||
}, | ||
}, | ||
{ | ||
...odinOrder, | ||
isShipped: features.emptyValue, | ||
// eslint-disable-next-line @typescript-eslint/camelcase | ||
shipment_id: features.emptyValue, | ||
customer: { | ||
...odin, | ||
parentId: 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({ | ||
description: "Thor's Mjolnir", | ||
customerId: thor.id, | ||
}); | ||
const odinOrder = await orderRepo.create({ | ||
description: "Odin's Coffee Maker", | ||
customerId: odin.id, | ||
}); | ||
|
||
const result = await orderRepo.findById(odinOrder.id, { | ||
include: [{relation: 'customer'}], | ||
}); | ||
const expected = { | ||
...odinOrder, | ||
isShipped: features.emptyValue, | ||
// eslint-disable-next-line @typescript-eslint/camelcase | ||
shipment_id: features.emptyValue, | ||
customer: { | ||
...odin, | ||
parentId: features.emptyValue, | ||
}, | ||
}; | ||
expect(toJSON(result)).to.deepEqual(toJSON(expected)); | ||
}); | ||
|
||
it('throws error if the target repository does not have the registered resolver', async () => { | ||
await orderRepo.create({ | ||
description: 'shiba', | ||
customerId: existingCustomerId, | ||
}); | ||
// unregister the resolver | ||
orderRepo.inclusionResolvers.delete('customer'); | ||
|
||
await expect( | ||
orderRepo.find({include: [{relation: 'customer'}]}), | ||
).to.be.rejectedWith( | ||
`Invalid "filter.include" entries: {"relation":"customer"}`, | ||
); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.