@loopback/repository-tests 0.20.2 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/crud/relations/acceptance/belongs-to.inclusion-resolver.polymorphic.relation.acceptance.d.ts +2 -0
  2. package/dist/crud/relations/acceptance/belongs-to.inclusion-resolver.polymorphic.relation.acceptance.js +170 -0
  3. package/dist/crud/relations/acceptance/belongs-to.inclusion-resolver.polymorphic.relation.acceptance.js.map +1 -0
  4. package/dist/crud/relations/acceptance/belongs-to.polymorphic.relation.acceptance.d.ts +2 -0
  5. package/dist/crud/relations/acceptance/belongs-to.polymorphic.relation.acceptance.js +55 -0
  6. package/dist/crud/relations/acceptance/belongs-to.polymorphic.relation.acceptance.js.map +1 -0
  7. package/dist/crud/relations/acceptance/has-many-through-inclusion-resolver.polymorphic.acceptance.d.ts +2 -0
  8. package/dist/crud/relations/acceptance/has-many-through-inclusion-resolver.polymorphic.acceptance.js +170 -0
  9. package/dist/crud/relations/acceptance/has-many-through-inclusion-resolver.polymorphic.acceptance.js.map +1 -0
  10. package/dist/crud/relations/acceptance/has-many-through.relation.polymorphic.acceptance.d.ts +2 -0
  11. package/dist/crud/relations/acceptance/has-many-through.relation.polymorphic.acceptance.js +361 -0
  12. package/dist/crud/relations/acceptance/has-many-through.relation.polymorphic.acceptance.js.map +1 -0
  13. package/dist/crud/relations/acceptance/has-one.inclusion-resolver.polymorphic.acceptance.d.ts +2 -0
  14. package/dist/crud/relations/acceptance/has-one.inclusion-resolver.polymorphic.acceptance.js +161 -0
  15. package/dist/crud/relations/acceptance/has-one.inclusion-resolver.polymorphic.acceptance.js.map +1 -0
  16. package/dist/crud/relations/acceptance/has-one.relation.polymorphic.acceptance.d.ts +2 -0
  17. package/dist/crud/relations/acceptance/has-one.relation.polymorphic.acceptance.js +190 -0
  18. package/dist/crud/relations/acceptance/has-one.relation.polymorphic.acceptance.js.map +1 -0
  19. package/dist/crud/relations/fixtures/models/contact.model.d.ts +18 -0
  20. package/dist/crud/relations/fixtures/models/contact.model.js +49 -0
  21. package/dist/crud/relations/fixtures/models/contact.model.js.map +1 -0
  22. package/dist/crud/relations/fixtures/models/customer-promotion-link.model.d.ts +14 -0
  23. package/dist/crud/relations/fixtures/models/customer-promotion-link.model.js +46 -0
  24. package/dist/crud/relations/fixtures/models/customer-promotion-link.model.js.map +1 -0
  25. package/dist/crud/relations/fixtures/models/customer.model.d.ts +16 -3
  26. package/dist/crud/relations/fixtures/models/customer.model.js +27 -9
  27. package/dist/crud/relations/fixtures/models/customer.model.js.map +1 -1
  28. package/dist/crud/relations/fixtures/models/index.d.ts +6 -0
  29. package/dist/crud/relations/fixtures/models/index.js +6 -0
  30. package/dist/crud/relations/fixtures/models/index.js.map +1 -1
  31. package/dist/crud/relations/fixtures/models/payment-method.model.d.ts +47 -0
  32. package/dist/crud/relations/fixtures/models/payment-method.model.js +87 -0
  33. package/dist/crud/relations/fixtures/models/payment-method.model.js.map +1 -0
  34. package/dist/crud/relations/fixtures/models/promotion.model.d.ts +27 -0
  35. package/dist/crud/relations/fixtures/models/promotion.model.js +54 -0
  36. package/dist/crud/relations/fixtures/models/promotion.model.js.map +1 -0
  37. package/dist/crud/relations/fixtures/models/stakeholder.model.d.ts +7 -0
  38. package/dist/crud/relations/fixtures/models/stakeholder.model.js +38 -0
  39. package/dist/crud/relations/fixtures/models/stakeholder.model.js.map +1 -0
  40. package/dist/crud/relations/fixtures/models/supplier.model.d.ts +15 -0
  41. package/dist/crud/relations/fixtures/models/supplier.model.js +23 -0
  42. package/dist/crud/relations/fixtures/models/supplier.model.js.map +1 -0
  43. package/dist/crud/relations/fixtures/repositories/contact.repository.d.ts +30 -0
  44. package/dist/crud/relations/fixtures/repositories/contact.repository.js +21 -0
  45. package/dist/crud/relations/fixtures/repositories/contact.repository.js.map +1 -0
  46. package/dist/crud/relations/fixtures/repositories/customer-promotion-link.repository.d.ts +26 -0
  47. package/dist/crud/relations/fixtures/repositories/customer-promotion-link.repository.js +18 -0
  48. package/dist/crud/relations/fixtures/repositories/customer-promotion-link.repository.js.map +1 -0
  49. package/dist/crud/relations/fixtures/repositories/customer.repository.d.ts +9 -2
  50. package/dist/crud/relations/fixtures/repositories/customer.repository.js +7 -1
  51. package/dist/crud/relations/fixtures/repositories/customer.repository.js.map +1 -1
  52. package/dist/crud/relations/fixtures/repositories/index.d.ts +6 -1
  53. package/dist/crud/relations/fixtures/repositories/index.js +6 -1
  54. package/dist/crud/relations/fixtures/repositories/index.js.map +1 -1
  55. package/dist/crud/relations/fixtures/repositories/payment-method.repository.d.ts +77 -0
  56. package/dist/crud/relations/fixtures/repositories/payment-method.repository.js +45 -0
  57. package/dist/crud/relations/fixtures/repositories/payment-method.repository.js.map +1 -0
  58. package/dist/crud/relations/fixtures/repositories/promotion.repository.d.ts +49 -0
  59. package/dist/crud/relations/fixtures/repositories/promotion.repository.js +25 -0
  60. package/dist/crud/relations/fixtures/repositories/promotion.repository.js.map +1 -0
  61. package/dist/crud/relations/fixtures/repositories/supplier.repository.d.ts +28 -0
  62. package/dist/crud/relations/fixtures/repositories/supplier.repository.js +21 -0
  63. package/dist/crud/relations/fixtures/repositories/supplier.repository.js.map +1 -0
  64. package/dist/crud/relations/helpers.d.ts +9 -1
  65. package/dist/crud/relations/helpers.js +85 -10
  66. package/dist/crud/relations/helpers.js.map +1 -1
  67. package/dist/helpers.repository-tests.d.ts +1 -1
  68. package/dist/helpers.repository-tests.js.map +1 -1
  69. package/package.json +12 -12
  70. package/src/crud/relations/acceptance/belongs-to.inclusion-resolver.polymorphic.relation.acceptance.ts +221 -0
  71. package/src/crud/relations/acceptance/belongs-to.polymorphic.relation.acceptance.ts +88 -0
  72. package/src/crud/relations/acceptance/has-many-through-inclusion-resolver.polymorphic.acceptance.ts +255 -0
  73. package/src/crud/relations/acceptance/has-many-through.relation.polymorphic.acceptance.ts +494 -0
  74. package/src/crud/relations/acceptance/has-one.inclusion-resolver.polymorphic.acceptance.ts +208 -0
  75. package/src/crud/relations/acceptance/has-one.relation.polymorphic.acceptance.ts +328 -0
  76. package/src/crud/relations/fixtures/models/contact.model.ts +56 -0
  77. package/src/crud/relations/fixtures/models/customer-promotion-link.model.ts +52 -0
  78. package/src/crud/relations/fixtures/models/customer.model.ts +39 -9
  79. package/src/crud/relations/fixtures/models/index.ts +6 -0
  80. package/src/crud/relations/fixtures/models/payment-method.model.ts +118 -0
  81. package/src/crud/relations/fixtures/models/promotion.model.ts +67 -0
  82. package/src/crud/relations/fixtures/models/stakeholder.model.ts +33 -0
  83. package/src/crud/relations/fixtures/models/supplier.model.ts +35 -0
  84. package/src/crud/relations/fixtures/repositories/contact.repository.ts +44 -0
  85. package/src/crud/relations/fixtures/repositories/customer-promotion-link.repository.ts +20 -0
  86. package/src/crud/relations/fixtures/repositories/customer.repository.ts +47 -0
  87. package/src/crud/relations/fixtures/repositories/index.ts +6 -1
  88. package/src/crud/relations/fixtures/repositories/payment-method.repository.ts +118 -0
  89. package/src/crud/relations/fixtures/repositories/promotion.repository.ts +37 -0
  90. package/src/crud/relations/fixtures/repositories/supplier.repository.ts +40 -0
  91. package/src/crud/relations/helpers.ts +200 -30
  92. package/src/helpers.repository-tests.ts +1 -1
@@ -0,0 +1,494 @@
1
+ // Copyright IBM Corp. 2020. All Rights Reserved.
2
+ // Node module: @loopback/repository-tests
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import {expect, toJSON} from '@loopback/testlab';
7
+ import {
8
+ CrudFeatures,
9
+ CrudRepositoryCtor,
10
+ CrudTestContext,
11
+ DataSourceOptions,
12
+ } from '../../..';
13
+ import {
14
+ deleteAllModelsInDefaultDataSource,
15
+ MixedIdType,
16
+ withCrudCtx,
17
+ } from '../../../helpers.repository-tests';
18
+ import {
19
+ Customer,
20
+ CustomerPromotionLink,
21
+ CustomerPromotionLinkRepository,
22
+ CustomerRepository,
23
+ FreeDelivery,
24
+ FreeDeliveryRepository,
25
+ HalfPrice,
26
+ HalfPriceRepository,
27
+ } from '../fixtures/models';
28
+ import {givenBoundCrudRepositories} from '../helpers';
29
+
30
+ export function hasManyThroughRelationAcceptance(
31
+ dataSourceOptions: DataSourceOptions,
32
+ repositoryClass: CrudRepositoryCtor,
33
+ features: CrudFeatures,
34
+ ) {
35
+ describe('HasManyThrough relation polymorphic (acceptance)', () => {
36
+ before(deleteAllModelsInDefaultDataSource);
37
+ let customerRepo: CustomerRepository;
38
+ let freeDeliveryRepo: FreeDeliveryRepository;
39
+ let halfPriceRepo: HalfPriceRepository;
40
+ let customerPromotionLinkRepo: CustomerPromotionLinkRepository;
41
+ let existingCustomerId: MixedIdType;
42
+
43
+ before(
44
+ withCrudCtx(async function setupRepository(ctx: CrudTestContext) {
45
+ ({
46
+ customerRepo,
47
+ freeDeliveryRepo,
48
+ halfPriceRepo,
49
+ customerPromotionLinkRepo,
50
+ } = givenBoundCrudRepositories(
51
+ ctx.dataSource,
52
+ repositoryClass,
53
+ features,
54
+ ));
55
+ await ctx.dataSource.automigrate([
56
+ Customer.name,
57
+ FreeDelivery.name,
58
+ HalfPrice.name,
59
+ CustomerPromotionLink.name,
60
+ ]);
61
+ }),
62
+ );
63
+
64
+ beforeEach(async () => {
65
+ await customerRepo.deleteAll();
66
+ await freeDeliveryRepo.deleteAll();
67
+ await halfPriceRepo.deleteAll();
68
+ await customerPromotionLinkRepo.deleteAll();
69
+ });
70
+
71
+ beforeEach(async () => {
72
+ existingCustomerId = (await givenPersistedCustomerInstance()).id;
73
+ });
74
+
75
+ it('creates an instance of the related model alone with a through model', async () => {
76
+ const promo = await customerRepo.promotions(existingCustomerId).create(
77
+ {description: 'free delivery promo'},
78
+ {
79
+ throughData: {
80
+ description: 'a through model',
81
+ promotiontype: 'FreeDelivery',
82
+ },
83
+ polymorphicType: 'FreeDelivery',
84
+ },
85
+ );
86
+
87
+ expect(toJSON(promo)).containDeep(
88
+ toJSON({
89
+ id: promo.id,
90
+ description: 'free delivery promo',
91
+ }),
92
+ );
93
+
94
+ const persistedPromo = await freeDeliveryRepo.findById(promo.id);
95
+ expect(toJSON(persistedPromo)).to.deepEqual(toJSON(promo));
96
+ const persistedLink = await customerPromotionLinkRepo.find();
97
+ expect(toJSON(persistedLink[0])).to.containDeep(
98
+ toJSON({
99
+ customerId: existingCustomerId,
100
+ // eslint-disable-next-line @typescript-eslint/naming-convention
101
+ promotion_id: promo.id,
102
+ description: 'a through model',
103
+ }),
104
+ );
105
+ });
106
+
107
+ it('finds instances of the related model', async () => {
108
+ const freeDelivery = await customerRepo
109
+ .promotions(existingCustomerId)
110
+ .create(
111
+ {description: 'free delivery promo'},
112
+ {
113
+ throughData: {
114
+ description: 'a through model',
115
+ promotiontype: 'FreeDelivery',
116
+ },
117
+ polymorphicType: 'FreeDelivery',
118
+ },
119
+ );
120
+ const notMyPromo = await freeDeliveryRepo.create({
121
+ description: "someone else's promo",
122
+ });
123
+ const notMyPromo2 = await halfPriceRepo.create({
124
+ description: "someone else's promo 2",
125
+ });
126
+ const result = await customerRepo
127
+ .promotions(existingCustomerId)
128
+ .find(undefined, {
129
+ throughOptions: {discriminator: 'promotiontype'},
130
+ polymorphicType: ['FreeDelivery'],
131
+ });
132
+ expect(toJSON(result)).to.containDeep(toJSON([freeDelivery]));
133
+ expect(toJSON(result[0])).to.not.containEql(toJSON(notMyPromo));
134
+ expect(toJSON(result[0])).to.not.containEql(toJSON(notMyPromo2));
135
+ });
136
+
137
+ it('patches instances', async () => {
138
+ const promo1 = await customerRepo.promotions(existingCustomerId).create(
139
+ {description: 'free delivery promo'},
140
+ {
141
+ throughData: {
142
+ description: 'a through model',
143
+ promotiontype: 'FreeDelivery',
144
+ },
145
+ polymorphicType: 'FreeDelivery',
146
+ },
147
+ );
148
+ const promo2 = await customerRepo.promotions(existingCustomerId).create(
149
+ {description: 'half price promo 2'},
150
+ {
151
+ throughData: {
152
+ description: 'a through model',
153
+ promotiontype: 'HalfPrice',
154
+ },
155
+ polymorphicType: 'HalfPrice',
156
+ },
157
+ );
158
+ const promo3 = await customerRepo.promotions(existingCustomerId).create(
159
+ {description: 'free delivery promo 3'},
160
+ {
161
+ throughData: {
162
+ description: 'a through model',
163
+ promotiontype: 'FreeDelivery',
164
+ },
165
+ polymorphicType: 'FreeDelivery',
166
+ },
167
+ );
168
+ const count = await customerRepo.promotions(existingCustomerId).patch(
169
+ {
170
+ FreeDelivery: {description: 'updated free delivery'},
171
+ HalfPrice: {description: 'updated half price'},
172
+ },
173
+ undefined,
174
+ {
175
+ throughOptions: {discriminator: 'promotiontype'},
176
+ isPolymorphic: true,
177
+ },
178
+ );
179
+
180
+ expect(count.count).to.equal(3);
181
+ const result = await customerRepo
182
+ .promotions(existingCustomerId)
183
+ .find(undefined, {
184
+ throughOptions: {discriminator: 'promotiontype'},
185
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
186
+ });
187
+ expect(toJSON(result)).to.containDeep(
188
+ toJSON([
189
+ {id: promo1.id, description: 'updated free delivery'},
190
+ {id: promo2.id, description: 'updated half price'},
191
+ {id: promo3.id, description: 'updated free delivery'},
192
+ ]),
193
+ );
194
+ });
195
+
196
+ it('patches an instance based on the filter', async () => {
197
+ const promo1 = await customerRepo.promotions(existingCustomerId).create(
198
+ {description: 'promo group 1'},
199
+ {
200
+ throughData: {
201
+ description: 'a through model',
202
+ promotiontype: 'FreeDelivery',
203
+ },
204
+ polymorphicType: 'FreeDelivery',
205
+ },
206
+ );
207
+ const promo2 = await customerRepo.promotions(existingCustomerId).create(
208
+ {description: 'promo group 2'},
209
+ {
210
+ throughData: {
211
+ description: 'a through model',
212
+ promotiontype: 'FreeDelivery',
213
+ },
214
+ polymorphicType: 'FreeDelivery',
215
+ },
216
+ );
217
+ const promo3 = await customerRepo.promotions(existingCustomerId).create(
218
+ {description: 'promo group 1'},
219
+ {
220
+ throughData: {
221
+ description: 'a through model',
222
+ promotiontype: 'HalfPrice',
223
+ },
224
+ polymorphicType: 'HalfPrice',
225
+ },
226
+ );
227
+ const count = await customerRepo.promotions(existingCustomerId).patch(
228
+ {HalfPrice: {description: 'promo group 2'}},
229
+ {id: promo3.id},
230
+ {
231
+ throughOptions: {discriminator: 'promotiontype'},
232
+ isPolymorphic: true,
233
+ },
234
+ );
235
+
236
+ expect(count.count).to.equal(1);
237
+ const result = await customerRepo
238
+ .promotions(existingCustomerId)
239
+ .find(undefined, {
240
+ throughOptions: {discriminator: 'promotiontype'},
241
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
242
+ });
243
+ expect(toJSON(result)).to.containDeep(
244
+ toJSON([
245
+ {id: promo1.id, description: 'promo group 1'},
246
+ {id: promo2.id, description: 'promo group 2'},
247
+ {id: promo3.id, description: 'promo group 2'},
248
+ ]),
249
+ );
250
+ });
251
+
252
+ it('throws error when query tries to change the target id', async () => {
253
+ // a diff id for CartItem instance
254
+ const anotherId = (await givenPersistedCustomerInstance()).id;
255
+ await customerRepo.promotions(existingCustomerId).create(
256
+ {description: 'promo group 1'},
257
+ {
258
+ throughData: {
259
+ description: 'a through model',
260
+ promotiontype: 'HalfPrice',
261
+ },
262
+ polymorphicType: 'HalfPrice',
263
+ },
264
+ );
265
+
266
+ await expect(
267
+ customerRepo
268
+ .promotions(existingCustomerId)
269
+ .patch({HalfPrice: {id: anotherId}}, undefined, {
270
+ throughOptions: {discriminator: 'promotiontype'},
271
+ isPolymorphic: true,
272
+ }),
273
+ ).to.be.rejectedWith(/Property "id" cannot be changed!/);
274
+ });
275
+
276
+ it('deletes many instances and their through models', async () => {
277
+ await customerRepo.promotions(existingCustomerId).create(
278
+ {description: 'promo group 1'},
279
+ {
280
+ throughData: {
281
+ description: 'a through model to be deleted',
282
+ promotiontype: 'HalfPrice',
283
+ },
284
+ polymorphicType: 'HalfPrice',
285
+ },
286
+ );
287
+ await customerRepo.promotions(existingCustomerId).create(
288
+ {description: 'promo group 1'},
289
+ {
290
+ throughData: {
291
+ description: 'a through model to be deleted 2',
292
+ promotiontype: 'HalfPrice',
293
+ },
294
+ polymorphicType: 'HalfPrice',
295
+ },
296
+ );
297
+ await customerRepo.promotions(existingCustomerId).create(
298
+ {description: 'promo group 1'},
299
+ {
300
+ throughData: {
301
+ description: 'a through model to be deleted 3',
302
+ promotiontype: 'FreeDelivery',
303
+ },
304
+ polymorphicType: 'FreeDelivery',
305
+ },
306
+ );
307
+
308
+ let links = await customerPromotionLinkRepo.find();
309
+ let halfPrice = await halfPriceRepo.find();
310
+ let freeDelivery = await freeDeliveryRepo.find();
311
+ expect(links).have.length(3);
312
+ expect(halfPrice).have.length(2);
313
+ expect(freeDelivery).have.length(1);
314
+
315
+ await customerRepo.promotions(existingCustomerId).delete(undefined, {
316
+ throughOptions: {discriminator: 'promotiontype'},
317
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
318
+ });
319
+ links = await customerPromotionLinkRepo.find();
320
+ halfPrice = await halfPriceRepo.find();
321
+ freeDelivery = await freeDeliveryRepo.find();
322
+ expect(halfPrice).have.length(0);
323
+ expect(freeDelivery).have.length(0);
324
+ expect(links).have.length(0);
325
+ });
326
+
327
+ it('deletes corresponding through models when the target gets deleted', async () => {
328
+ const promotion = await customerRepo
329
+ .promotions(existingCustomerId)
330
+ .create(
331
+ {description: 'promo group 1'},
332
+ {
333
+ throughData: {
334
+ description: 'a through model to be deleted',
335
+ promotiontype: 'HalfPrice',
336
+ },
337
+ polymorphicType: 'HalfPrice',
338
+ },
339
+ );
340
+ const anotherId = (await givenPersistedCustomerInstance()).id;
341
+ // another through model that links to the same item
342
+ await customerPromotionLinkRepo.create({
343
+ customerId: anotherId,
344
+ // eslint-disable-next-line @typescript-eslint/naming-convention
345
+ promotion_id: promotion.id,
346
+ promotiontype: 'HalfPrice',
347
+ });
348
+
349
+ let links = await customerPromotionLinkRepo.find();
350
+ let halfPrice = await halfPriceRepo.find();
351
+ expect(links).have.length(2);
352
+ expect(halfPrice).have.length(1);
353
+
354
+ await customerRepo.promotions(existingCustomerId).delete(undefined, {
355
+ throughOptions: {discriminator: 'promotiontype'},
356
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
357
+ });
358
+ links = await customerPromotionLinkRepo.find();
359
+ halfPrice = await halfPriceRepo.find();
360
+ expect(links).have.length(0);
361
+ expect(halfPrice).have.length(0);
362
+ });
363
+
364
+ it('deletes instances based on the filter', async () => {
365
+ await customerRepo.promotions(existingCustomerId).create(
366
+ {description: 'promo group 1'},
367
+ {
368
+ throughData: {
369
+ description: 'a through model to be deleted',
370
+ promotiontype: 'HalfPrice',
371
+ },
372
+ polymorphicType: 'HalfPrice',
373
+ },
374
+ );
375
+ await customerRepo.promotions(existingCustomerId).create(
376
+ {description: 'promo group 2'},
377
+ {
378
+ throughData: {
379
+ description: 'a through model to be deleted',
380
+ promotiontype: 'FreeDelivery',
381
+ },
382
+ polymorphicType: 'FreeDelivery',
383
+ },
384
+ );
385
+
386
+ let links = await customerPromotionLinkRepo.find();
387
+ let freeDelivery = await freeDeliveryRepo.find();
388
+ let halfPrice = await halfPriceRepo.find();
389
+ expect(links).have.length(2);
390
+ expect(freeDelivery).have.length(1);
391
+ expect(halfPrice).have.length(1);
392
+
393
+ await customerRepo.promotions(existingCustomerId).delete(
394
+ {description: 'does not exist'},
395
+ {
396
+ throughOptions: {discriminator: 'promotiontype'},
397
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
398
+ },
399
+ );
400
+ links = await customerPromotionLinkRepo.find();
401
+ freeDelivery = await freeDeliveryRepo.find();
402
+ halfPrice = await halfPriceRepo.find();
403
+ expect(links).have.length(2);
404
+ expect(freeDelivery).have.length(1);
405
+ expect(halfPrice).have.length(1);
406
+
407
+ await customerRepo.promotions(existingCustomerId).delete(
408
+ {description: 'promo group 2'},
409
+ {
410
+ throughOptions: {discriminator: 'promotiontype'},
411
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
412
+ },
413
+ );
414
+ links = await customerPromotionLinkRepo.find();
415
+ freeDelivery = await freeDeliveryRepo.find();
416
+ halfPrice = await halfPriceRepo.find();
417
+ expect(links).have.length(1);
418
+ expect(freeDelivery).have.length(0);
419
+ expect(halfPrice).have.length(1);
420
+ });
421
+
422
+ it('links a target model to a source model', async () => {
423
+ const freeDelivery = await freeDeliveryRepo.create({
424
+ description: 'a free delivery promotion',
425
+ });
426
+
427
+ let targets = await customerRepo
428
+ .promotions(existingCustomerId)
429
+ .find(undefined, {
430
+ throughOptions: {discriminator: 'promotiontype'},
431
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
432
+ });
433
+ expect(targets).to.be.empty();
434
+
435
+ await customerRepo
436
+ .promotions(existingCustomerId)
437
+ .link(freeDelivery.id, {throughData: {promotiontype: 'FreeDelivery'}});
438
+
439
+ targets = await customerRepo
440
+ .promotions(existingCustomerId)
441
+ .find(undefined, {
442
+ throughOptions: {discriminator: 'promotiontype'},
443
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
444
+ });
445
+ expect(targets).to.deepEqual([freeDelivery]);
446
+
447
+ const link = await customerPromotionLinkRepo.find();
448
+ expect(toJSON(link[0])).to.containEql(
449
+ // eslint-disable-next-line @typescript-eslint/naming-convention
450
+ toJSON({customerId: existingCustomerId, promotion_id: freeDelivery.id}),
451
+ );
452
+ });
453
+
454
+ it('unlinks a target model from a source model', async () => {
455
+ const promotion1 = await customerRepo
456
+ .promotions(existingCustomerId)
457
+ .create(
458
+ {description: 'promo group 1'},
459
+ {
460
+ throughData: {
461
+ description: 'a through model to be deleted',
462
+ promotiontype: 'HalfPrice',
463
+ },
464
+ polymorphicType: 'HalfPrice',
465
+ },
466
+ );
467
+
468
+ let targets = await customerRepo
469
+ .promotions(existingCustomerId)
470
+ .find(undefined, {
471
+ throughOptions: {discriminator: 'promotiontype'},
472
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
473
+ });
474
+ expect(targets).to.deepEqual([promotion1]);
475
+
476
+ await customerRepo.promotions(existingCustomerId).unlink(promotion1.id);
477
+
478
+ targets = await customerRepo
479
+ .promotions(existingCustomerId)
480
+ .find(undefined, {
481
+ throughOptions: {discriminator: 'promotiontype'},
482
+ polymorphicType: ['HalfPrice', 'FreeDelivery'],
483
+ });
484
+ expect(targets).to.be.empty();
485
+
486
+ const link = await customerPromotionLinkRepo.find();
487
+ expect(link).to.be.empty();
488
+ });
489
+
490
+ async function givenPersistedCustomerInstance() {
491
+ return customerRepo.create({name: 'a customer'});
492
+ }
493
+ });
494
+ }
@@ -0,0 +1,208 @@
1
+ // Copyright IBM Corp. 2019,2020. All Rights Reserved.
2
+ // Node module: @loopback/repository-tests
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import {expect, skipIf, toJSON} from '@loopback/testlab';
7
+ import {Suite} from 'mocha';
8
+ import {
9
+ CrudFeatures,
10
+ CrudRepositoryCtor,
11
+ CrudTestContext,
12
+ DataSourceOptions,
13
+ } from '../../..';
14
+ import {
15
+ deleteAllModelsInDefaultDataSource,
16
+ withCrudCtx,
17
+ } from '../../../helpers.repository-tests';
18
+ import {
19
+ CardInfoRepository,
20
+ Cash,
21
+ CashRepository,
22
+ CreditCard,
23
+ CreditCardRepository,
24
+ Customer,
25
+ CustomerRepository,
26
+ } from '../fixtures/models';
27
+ import {givenBoundCrudRepositories} from '../helpers';
28
+
29
+ export function hasOneInclusionResolverPolymorphicAcceptance(
30
+ dataSourceOptions: DataSourceOptions,
31
+ repositoryClass: CrudRepositoryCtor,
32
+ features: CrudFeatures,
33
+ ) {
34
+ skipIf<[(this: Suite) => void], void>(
35
+ !features.supportsInclusionResolvers,
36
+ describe,
37
+ 'HasOne inclusion resolvers - polymorphic - acceptance',
38
+ suite,
39
+ );
40
+ function suite() {
41
+ before(deleteAllModelsInDefaultDataSource);
42
+ let customerRepo: CustomerRepository;
43
+ let creditCardRepo: CreditCardRepository;
44
+ let cashRepo: CashRepository;
45
+ let cardInfoRepo: CardInfoRepository;
46
+
47
+ before(
48
+ withCrudCtx(async function setupRepository(ctx: CrudTestContext) {
49
+ // this helper should create the inclusion resolvers and also
50
+ // register inclusion resolvers for us
51
+ ({customerRepo, creditCardRepo, cashRepo, cardInfoRepo} =
52
+ givenBoundCrudRepositories(
53
+ ctx.dataSource,
54
+ repositoryClass,
55
+ features,
56
+ ));
57
+ expect(customerRepo.paymentMethod.inclusionResolver).to.be.Function();
58
+
59
+ await ctx.dataSource.automigrate([
60
+ Customer.name,
61
+ CreditCard.name,
62
+ Cash.name,
63
+ ]);
64
+ }),
65
+ );
66
+
67
+ beforeEach(async () => {
68
+ await customerRepo.deleteAll();
69
+ await creditCardRepo.deleteAll();
70
+ await cashRepo.deleteAll();
71
+ await cardInfoRepo.deleteAll();
72
+ });
73
+
74
+ it('throws an error if it tries to query nonexistent relation names', async () => {
75
+ const customer = await customerRepo.create({
76
+ name: 'customer',
77
+ paymentMethodType: 'CreditCard',
78
+ });
79
+ await creditCardRepo.create({
80
+ customerId: customer.id,
81
+ });
82
+ await expect(customerRepo.find({include: ['home']})).to.be.rejectedWith(
83
+ `Invalid "filter.include" entries: "home"`,
84
+ );
85
+ });
86
+
87
+ it('returns single model instance including single related instance', async () => {
88
+ const thor = await customerRepo.create({
89
+ name: 'Thor',
90
+ paymentMethodType: 'CreditCard',
91
+ });
92
+ const thorCreditCard = await creditCardRepo.create({
93
+ customerId: thor.id,
94
+ });
95
+ const result = await customerRepo.find({
96
+ include: ['paymentMethod'],
97
+ });
98
+
99
+ const expected = {
100
+ ...thor,
101
+ parentId: features.emptyValue,
102
+ paymentMethod: thorCreditCard,
103
+ };
104
+ expect(toJSON(result)).to.deepEqual([toJSON(expected)]);
105
+ });
106
+
107
+ it('returns multiple model instances including related instances', async () => {
108
+ const thor = await customerRepo.create({
109
+ name: 'Thor',
110
+ paymentMethodType: 'Cash',
111
+ });
112
+ const odin = await customerRepo.create({
113
+ name: 'Odin',
114
+ paymentMethodType: 'CreditCard',
115
+ });
116
+ const thorCash = await cashRepo.create({
117
+ customerId: thor.id,
118
+ });
119
+ const odinCreditCard = await creditCardRepo.create({
120
+ customerId: odin.id,
121
+ });
122
+
123
+ const result = await customerRepo.find({
124
+ include: ['paymentMethod'],
125
+ });
126
+
127
+ const expected = [
128
+ {
129
+ ...thor,
130
+ parentId: features.emptyValue,
131
+ paymentMethod: thorCash,
132
+ },
133
+ {
134
+ ...odin,
135
+ parentId: features.emptyValue,
136
+ paymentMethod: odinCreditCard,
137
+ },
138
+ ];
139
+ expect(toJSON(result)).to.deepEqual(toJSON(expected));
140
+ });
141
+
142
+ it('returns a specified instance including its related model instance', async () => {
143
+ const thor = await customerRepo.create({
144
+ name: 'Thor',
145
+ paymentMethodType: 'Cash',
146
+ });
147
+ const odin = await customerRepo.create({
148
+ name: 'Odin',
149
+ paymentMethodType: 'CreditCard',
150
+ });
151
+ const bella = await customerRepo.create({
152
+ name: 'Bella',
153
+ paymentMethodType: 'CreditCard',
154
+ });
155
+ await cashRepo.create({
156
+ customerId: thor.id,
157
+ });
158
+ await creditCardRepo.create({
159
+ customerId: odin.id,
160
+ });
161
+ const bellaPaymentMethod = await creditCardRepo.create({
162
+ customerId: bella.id,
163
+ });
164
+ const bellaCardInfo = await creditCardRepo
165
+ .cardInfo(bellaPaymentMethod.id)
166
+ .create({cardHolder: 'bella!'});
167
+ bellaPaymentMethod.cardInfo = bellaCardInfo;
168
+ const result = await customerRepo.findById(bella.id, {
169
+ include: [
170
+ {
171
+ relation: 'paymentMethod',
172
+ scope: {
173
+ include: [
174
+ {
175
+ relation: 'cardInfo',
176
+ targetType: 'CreditCard',
177
+ },
178
+ ],
179
+ },
180
+ },
181
+ ],
182
+ });
183
+
184
+ const expected = {
185
+ ...bella,
186
+ parentId: features.emptyValue,
187
+ paymentMethod: bellaPaymentMethod,
188
+ };
189
+ expect(toJSON(result)).to.deepEqual(toJSON(expected));
190
+ });
191
+
192
+ it('throws error if the target repository does not have the registered resolver', async () => {
193
+ const customer = await customerRepo.create({
194
+ name: 'customer',
195
+ paymentMethodType: 'CreditCard',
196
+ });
197
+ await creditCardRepo.create({
198
+ customerId: customer.id,
199
+ });
200
+ // unregister the resolver
201
+ customerRepo.inclusionResolvers.delete('paymentMethod');
202
+
203
+ await expect(
204
+ customerRepo.find({include: ['paymentMethod']}),
205
+ ).to.be.rejectedWith(`Invalid "filter.include" entries: "paymentMethod"`);
206
+ });
207
+ }
208
+ }