-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[WIP] feat(repository): hasManyThrough #4438
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6eccb8d
d41faf0
b781b77
c5d21c5
088e7b5
66c0ab9
3831a1e
f1cccf9
c50841c
2b94fd9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| // Copyright IBM Corp. 2020. 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, toJSON} from '@loopback/testlab'; | ||
| import { | ||
| CrudFeatures, | ||
| CrudRepositoryCtor, | ||
| CrudTestContext, | ||
| DataSourceOptions, | ||
| } from '../../..'; | ||
| import { | ||
| deleteAllModelsInDefaultDataSource, | ||
| MixedIdType, | ||
| withCrudCtx, | ||
| } from '../../../helpers.repository-tests'; | ||
| import { | ||
| Customer, | ||
| CustomerRepository, | ||
| Order, | ||
| OrderRepository, | ||
| Seller, | ||
| SellerRepository, | ||
| } from '../fixtures/models'; | ||
| import {givenBoundCrudRepositories} from '../helpers'; | ||
|
|
||
| export function hasManyThroughRelationAcceptance( | ||
| dataSourceOptions: DataSourceOptions, | ||
| repositoryClass: CrudRepositoryCtor, | ||
| features: CrudFeatures, | ||
| ) { | ||
| describe('HasManyThrough relation (acceptance)', () => { | ||
| before(deleteAllModelsInDefaultDataSource); | ||
| let customerRepo: CustomerRepository; | ||
| let orderRepo: OrderRepository; | ||
| let sellerRepo: SellerRepository; | ||
| let existingCustomerId: MixedIdType; | ||
|
|
||
| before( | ||
| withCrudCtx(async function setupRepository(ctx: CrudTestContext) { | ||
| ({customerRepo, orderRepo, sellerRepo} = givenBoundCrudRepositories( | ||
| ctx.dataSource, | ||
| repositoryClass, | ||
| features, | ||
| )); | ||
| await ctx.dataSource.automigrate([ | ||
| Customer.name, | ||
| Order.name, | ||
| Seller.name, | ||
| ]); | ||
| }), | ||
| ); | ||
|
|
||
| beforeEach(async () => { | ||
| await customerRepo.deleteAll(); | ||
| await orderRepo.deleteAll(); | ||
| await sellerRepo.deleteAll(); | ||
| }); | ||
|
|
||
| beforeEach(async () => { | ||
| existingCustomerId = (await givenPersistedCustomerInstance()).id; | ||
| }); | ||
|
|
||
| it('can create related models', async () => { | ||
| // TODO(derdeka): creating seller though order is not the best example usecase - find alternative | ||
| const sellerData: Partial<Seller> = { | ||
| name: 'Domino’s Pizza', | ||
| }; | ||
| const orderData: Partial<Order> = { | ||
| description: 'pizza', | ||
| }; | ||
| const seller = await customerRepo | ||
| .sellers(existingCustomerId) | ||
| .create(sellerData, { | ||
| throughData: orderData, | ||
| }); | ||
| expect(toJSON(seller)).containDeep(toJSON(sellerData)); | ||
|
Comment on lines
+73
to
+78
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Creating a
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about Category-Product domain, where each category can have multiple products and each product can belong to multiple categories? The "through" model can be
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, we can reverse the relation to "Seller has many Customer through Order". I think in such case it's more reasonable to create a new customer via the seller? |
||
|
|
||
| // check target object | ||
| const persistedSeller = await sellerRepo.findById(seller.id); | ||
| expect(toJSON(persistedSeller)).containDeep(toJSON(sellerData)); | ||
|
|
||
| // check through object | ||
| const persistedOrders: Order[] = await orderRepo.find({ | ||
| where: { | ||
| sellerId: seller.id, | ||
| customerId: existingCustomerId, | ||
| }, | ||
| }); | ||
| expect(persistedOrders.length).to.eql(1); | ||
| expect(persistedOrders[0]).containDeep(toJSON(orderData)); | ||
| }); | ||
|
|
||
| async function givenPersistedCustomerInstance() { | ||
| return customerRepo.create({name: 'a customer'}); | ||
| } | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| // Copyright IBM Corp. 2020. 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 { | ||
| Entity, | ||
| EntityCrudRepository, | ||
| hasMany, | ||
| HasManyThroughRepositoryFactory, | ||
| model, | ||
| property, | ||
| } from '@loopback/repository'; | ||
| import {Customer, CustomerWithRelations} from './customer.model'; | ||
| import {Order} from './order.model'; | ||
|
|
||
| @model() | ||
| export class Seller extends Entity { | ||
| @property({ | ||
| type: 'number', | ||
| id: true, | ||
| }) | ||
| id: number; | ||
|
|
||
| @property({ | ||
| type: 'string', | ||
| required: true, | ||
| }) | ||
| name: string; | ||
|
|
||
| @hasMany(() => Customer, { | ||
| through: { | ||
| model: () => Order, | ||
| }, | ||
| }) | ||
| customers?: Customer[]; | ||
| } | ||
|
|
||
| export interface SellerRelations { | ||
| customers?: CustomerWithRelations; | ||
| } | ||
|
|
||
| export type SellerWithRelations = Seller & SellerRelations; | ||
|
|
||
| export interface SellerRepository | ||
| extends EntityCrudRepository<Seller, typeof Seller.prototype.id> { | ||
| // define additional members like relation methods here | ||
| customers: HasManyThroughRepositoryFactory< | ||
| Customer, | ||
| typeof Customer.prototype.id, | ||
| Order, | ||
| typeof Seller.prototype.id | ||
| >; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| // Copyright IBM Corp. 2020. 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 {Getter} from '@loopback/context'; | ||
| import { | ||
| createHasManyThroughRepositoryFactory, | ||
| HasManyThroughDefinition, | ||
| HasManyThroughRepositoryFactory, | ||
| juggler, | ||
| } from '@loopback/repository'; | ||
| import {CrudRepositoryCtor} from '../../../../types.repository-tests'; | ||
| import {Customer, Order, Seller, SellerRelations} from '../models'; | ||
|
|
||
| // create the SellerRepo by calling this func so that it can be extended from CrudRepositoryCtor | ||
| export function createSellerRepo(repoClass: CrudRepositoryCtor) { | ||
| return class SellerRepository extends repoClass< | ||
| Seller, | ||
| typeof Seller.prototype.id, | ||
| SellerRelations | ||
| > { | ||
| public readonly customers: HasManyThroughRepositoryFactory< | ||
| Customer, | ||
| typeof Customer.prototype.id, | ||
| Order, | ||
| typeof Seller.prototype.id | ||
| >; | ||
|
|
||
| constructor( | ||
| db: juggler.DataSource, | ||
| customerRepositoryGetter: Getter<typeof repoClass.prototype>, | ||
| orderRepositoryGetter: Getter<typeof repoClass.prototype>, | ||
| ) { | ||
| super(Seller, db); | ||
| const customersMeta = this.entityClass.definition.relations['customers']; | ||
| this.customers = createHasManyThroughRepositoryFactory( | ||
| customersMeta as HasManyThroughDefinition, | ||
| customerRepositoryGetter, | ||
| orderRepositoryGetter, | ||
| ); | ||
| } | ||
| }; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.