Skip to content

Commit 001d970

Browse files
0x0aNLdhmlau
authored andcommitted
test(repository-tests): add test for bi-drectional hasManyThrough relation inclusion
Signed-off-by: Roderik van Heijst <rvanheijst@0x0a.nl>
1 parent 34f8201 commit 001d970

4 files changed

Lines changed: 108 additions & 5 deletions

File tree

packages/repository-tests/src/crud/relations/acceptance/has-many-through-inclusion-resolver.acceptance.ts

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,56 @@ export function hasManyThroughInclusionResolverAcceptance(
158158
expect(toJSON(result)).to.deepEqual(toJSON(expected));
159159
});
160160

161+
it('includes relations through a bi-directional hasManyThrough relation', async () => {
162+
const link = await customerRepo.create({name: 'Link'});
163+
const zelda = await customerRepo.create({name: 'Zelda'});
164+
const voldemort = await customerRepo.create({name: 'Voldemort'});
165+
166+
const cart = await customerRepo
167+
.cartItems(zelda.id)
168+
.create({description: 'crown'});
169+
await customerRepo.cartItems(link.id).create({description: 'shield'});
170+
await customerRepo
171+
.cartItems(link.id)
172+
.create({description: 'green hat'});
173+
174+
// Cart is now shared by Link, Zelda and Voldemort
175+
await customerRepo.cartItems(link.id).link(cart.id);
176+
await customerRepo.cartItems(voldemort.id).link(cart.id);
177+
178+
// We're testing the limit and order in the inclusion of customers
179+
// within the inclusion of cartItems. The order clause in combination
180+
// with the limit clause should return Zelda and Voldemort in that
181+
// order, excluding Link due to the limit of 2 and sort by name.
182+
const result = await customerRepo.find({
183+
limit: 1,
184+
order: ['name DESC'],
185+
include: [
186+
{
187+
relation: 'cartItems',
188+
scope: {
189+
include: [
190+
{
191+
relation: 'customers',
192+
scope: {limit: 2, order: ['name DESC']},
193+
},
194+
],
195+
},
196+
},
197+
],
198+
});
199+
const expected = [
200+
{
201+
...zelda,
202+
parentId: features.emptyValue,
203+
cartItems: [{...cart, customers: [zelda, voldemort]}],
204+
},
205+
];
206+
207+
expect(result.length).to.eql(1);
208+
expect(toJSON(result)).to.deepEqual(toJSON(expected));
209+
});
210+
161211
it('honours field scope when returning a model', async () => {
162212
const link = await customerRepo.create({name: 'Link'});
163213
const sword = await customerRepo
@@ -185,19 +235,27 @@ export function hasManyThroughInclusionResolverAcceptance(
185235
expect(toJSON(result)).to.deepEqual(toJSON(expected));
186236
});
187237

188-
it('honours limit scope when returning a model', async () => {
238+
it('honours limit and order scope when returning a model', async () => {
189239
const link = await customerRepo.create({name: 'Link'});
190240
await customerRepo
191241
.cartItems(link.id)
192242
.create({description: 'master sword'});
193-
await customerRepo.cartItems(link.id).create({description: 'shield'});
243+
const shield = await customerRepo
244+
.cartItems(link.id)
245+
.create({description: 'shield'});
194246

195247
const result = await customerRepo.find({
196-
include: [{relation: 'cartItems', scope: {limit: 1}}],
248+
include: [
249+
{
250+
relation: 'cartItems',
251+
scope: {limit: 1, order: ['description DESC']},
252+
},
253+
],
197254
});
198255

199256
expect(result.length).to.eql(1);
200257
expect(result[0].cartItems.length).to.eql(1);
258+
expect(result[0].cartItems[0]?.id).to.eql(shield?.id);
201259
});
202260

203261
it('finds models with nested inclusion', async () => {

packages/repository-tests/src/crud/relations/fixtures/models/cart-item.model.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import {
88
BelongsToAccessor,
99
Entity,
1010
EntityCrudRepository,
11+
hasMany,
12+
HasManyThroughRepositoryFactory,
1113
model,
1214
property,
1315
} from '@loopback/repository';
14-
import {Order, OrderWithRelations} from '.';
16+
import {Customer, CustomerCartItemLink, Order, OrderWithRelations} from '.';
1517
import {MixedIdType} from '../../../../helpers.repository-tests';
1618

1719
@model()
@@ -30,15 +32,25 @@ export class CartItem extends Entity {
3032

3133
@belongsTo(() => Order)
3234
orderId: MixedIdType;
35+
36+
@hasMany(() => Customer, {through: {model: () => CustomerCartItemLink}})
37+
customers: Customer[];
3338
}
3439

3540
export interface CartItemRelations {
3641
order?: OrderWithRelations[];
42+
customers?: Customer[];
3743
}
3844

3945
export type CartItemWithRelations = CartItem & CartItemRelations;
4046

4147
export interface CartItemRepository
4248
extends EntityCrudRepository<CartItem, typeof CartItem.prototype.id> {
4349
order: BelongsToAccessor<Order, MixedIdType>;
50+
customers: HasManyThroughRepositoryFactory<
51+
Customer,
52+
MixedIdType,
53+
CustomerCartItemLink,
54+
MixedIdType
55+
>;
4456
}

packages/repository-tests/src/crud/relations/fixtures/repositories/cart-item.repository.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,20 @@ import {
77
BelongsToAccessor,
88
BelongsToDefinition,
99
createBelongsToAccessor,
10+
createHasManyThroughRepositoryFactory,
1011
Getter,
12+
HasManyDefinition,
13+
HasManyThroughRepositoryFactory,
1114
juggler,
1215
} from '@loopback/repository';
1316
import {CrudRepositoryCtor} from '../../../..';
14-
import {CartItem, CartItemRelations, Order} from '../models';
17+
import {
18+
CartItem,
19+
CartItemRelations,
20+
Customer,
21+
CustomerCartItemLink,
22+
Order,
23+
} from '../models';
1524

1625
// create the CartItemRepo by calling this func so that it can be extended from CrudRepositoryCtor
1726
export function createCartItemRepo(repoClass: CrudRepositoryCtor) {
@@ -24,9 +33,19 @@ export function createCartItemRepo(repoClass: CrudRepositoryCtor) {
2433
Order,
2534
typeof CartItem.prototype.id
2635
>;
36+
37+
public readonly customers: HasManyThroughRepositoryFactory<
38+
Customer,
39+
typeof Customer.prototype.id,
40+
CustomerCartItemLink,
41+
typeof CustomerCartItemLink.prototype.id
42+
>;
43+
2744
constructor(
2845
db: juggler.DataSource,
2946
orderRepositoryGetter: Getter<typeof repoClass.prototype>,
47+
customerRepositoryGetter: Getter<typeof repoClass.prototype>,
48+
customerCartItemLinkRepositoryGetter: Getter<typeof repoClass.prototype>,
3049
) {
3150
super(CartItem, db);
3251
const ordersMeta = this.entityClass.definition.relations['order'];
@@ -35,6 +54,13 @@ export function createCartItemRepo(repoClass: CrudRepositoryCtor) {
3554
orderRepositoryGetter,
3655
this,
3756
);
57+
58+
const customersMeta = this.entityClass.definition.relations['customers'];
59+
this.customers = createHasManyThroughRepositoryFactory(
60+
customersMeta as HasManyDefinition,
61+
customerRepositoryGetter,
62+
customerCartItemLinkRepositoryGetter,
63+
);
3864
}
3965
};
4066
}

packages/repository-tests/src/crud/relations/helpers.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,20 @@ export function givenBoundCrudRepositories(
201201
const cartItemRepo: CartItemRepository = new cartItemRepoClass(
202202
db,
203203
async () => orderRepo,
204+
async () => customerRepo,
205+
async () => customerCartItemLinkRepo,
204206
);
205207

206208
cartItemRepo.inclusionResolvers.set(
207209
'order',
208210
cartItemRepo.order.inclusionResolver,
209211
);
210212

213+
cartItemRepo.inclusionResolvers.set(
214+
'customers',
215+
cartItemRepo.customers.inclusionResolver,
216+
);
217+
211218
const customerCartItemLinkRepoClass =
212219
createCustomerCartItemLinkRepo(repositoryClass);
213220
const customerCartItemLinkRepo: CustomerCartItemLinkRepository =

0 commit comments

Comments
 (0)