3
3
// This file is licensed under the MIT License.
4
4
// License text available at https://opensource.org/licenses/MIT
5
5
6
- import { expect } from '@loopback/testlab' ;
6
+ import { expect , toJSON } from '@loopback/testlab' ;
7
7
import {
8
8
bindModel ,
9
9
DefaultCrudRepository ,
@@ -15,6 +15,21 @@ import {
15
15
ModelDefinition ,
16
16
property ,
17
17
} from '../../..' ;
18
+ import {
19
+ belongsTo ,
20
+ BelongsToAccessor ,
21
+ BelongsToDefinition ,
22
+ createBelongsToAccessor ,
23
+ createHasManyRepositoryFactory ,
24
+ createHasOneRepositoryFactory ,
25
+ hasMany ,
26
+ HasManyDefinition ,
27
+ HasManyRepositoryFactory ,
28
+ hasOne ,
29
+ HasOneDefinition ,
30
+ HasOneRepositoryFactory ,
31
+ InclusionResolver ,
32
+ } from '../../../relations' ;
18
33
import { CrudConnectorStub } from '../crud-connector.stub' ;
19
34
const TransactionClass = require ( 'loopback-datasource-juggler' ) . Transaction ;
20
35
@@ -274,6 +289,190 @@ describe('DefaultCrudRepository', () => {
274
289
} ) ;
275
290
} ) ;
276
291
292
+ context ( 'find* methods including relations' , ( ) => {
293
+ @model ( )
294
+ class Author extends Entity {
295
+ @property ( { id : true } )
296
+ id ?: number ;
297
+ @property ( )
298
+ name : string ;
299
+ @belongsTo ( ( ) => Folder )
300
+ folderId : number ;
301
+ }
302
+
303
+ @model ( )
304
+ class Folder extends Entity {
305
+ @property ( { id : true } )
306
+ id ?: number ;
307
+ @property ( )
308
+ name : string ;
309
+ @hasMany ( ( ) => File )
310
+ files : File [ ] ;
311
+ @hasOne ( ( ) => Author )
312
+ author : Author ;
313
+ }
314
+
315
+ @model ( )
316
+ class File extends Entity {
317
+ @property ( { id : true } )
318
+ id ?: number ;
319
+ @property ( )
320
+ title : string ;
321
+ @belongsTo ( ( ) => Folder )
322
+ folderId : number ;
323
+ }
324
+
325
+ let folderRepo : DefaultCrudRepository < Folder , unknown , { } > ;
326
+ let fileRepo : DefaultCrudRepository < File , unknown , { } > ;
327
+ let authorRepo : DefaultCrudRepository < Author , unknown , { } > ;
328
+
329
+ let folderFiles : HasManyRepositoryFactory < File , typeof Folder . prototype . id > ;
330
+ let fileFolder : BelongsToAccessor < Folder , typeof File . prototype . id > ;
331
+ let folderAuthor : HasOneRepositoryFactory <
332
+ Author ,
333
+ typeof Folder . prototype . id
334
+ > ;
335
+
336
+ before ( ( ) => {
337
+ ds = new juggler . DataSource ( {
338
+ name : 'db' ,
339
+ connector : 'memory' ,
340
+ } ) ;
341
+ folderRepo = new DefaultCrudRepository ( Folder , ds ) ;
342
+ fileRepo = new DefaultCrudRepository ( File , ds ) ;
343
+ authorRepo = new DefaultCrudRepository ( Author , ds ) ;
344
+ } ) ;
345
+
346
+ before ( ( ) => {
347
+ // using a variable instead of a repository property
348
+ folderFiles = createHasManyRepositoryFactory (
349
+ Folder . definition . relations . files as HasManyDefinition ,
350
+ async ( ) => fileRepo ,
351
+ ) ;
352
+ folderAuthor = createHasOneRepositoryFactory (
353
+ Folder . definition . relations . author as HasOneDefinition ,
354
+ async ( ) => authorRepo ,
355
+ ) ;
356
+ fileFolder = createBelongsToAccessor (
357
+ File . definition . relations . folder as BelongsToDefinition ,
358
+ async ( ) => folderRepo ,
359
+ fileRepo ,
360
+ ) ;
361
+ } ) ;
362
+
363
+ beforeEach ( async ( ) => {
364
+ await folderRepo . deleteAll ( ) ;
365
+ await fileRepo . deleteAll ( ) ;
366
+ await authorRepo . deleteAll ( ) ;
367
+ } ) ;
368
+
369
+ it ( 'implements Repository.find() with included models' , async ( ) => {
370
+ const createdFolders = await folderRepo . createAll ( [
371
+ { name : 'f1' , id : 1 } ,
372
+ { name : 'f2' , id : 2 } ,
373
+ ] ) ;
374
+ const files = await fileRepo . createAll ( [
375
+ { id : 1 , title : 'file1' , folderId : 1 } ,
376
+ { id : 2 , title : 'file2' , folderId : 3 } ,
377
+ ] ) ;
378
+
379
+ folderRepo . registerInclusionResolver ( 'files' , hasManyResolver ) ;
380
+
381
+ const folders = await folderRepo . find ( { include : [ { relation : 'files' } ] } ) ;
382
+
383
+ expect ( toJSON ( folders ) ) . to . deepEqual ( [
384
+ { ...createdFolders [ 0 ] . toJSON ( ) , files : [ toJSON ( files [ 0 ] ) ] } ,
385
+ { ...createdFolders [ 1 ] . toJSON ( ) , files : [ ] } ,
386
+ ] ) ;
387
+ } ) ;
388
+
389
+ it ( 'implements Repository.findById() with included models' , async ( ) => {
390
+ const folders = await folderRepo . createAll ( [
391
+ { name : 'f1' , id : 1 } ,
392
+ { name : 'f2' , id : 2 } ,
393
+ ] ) ;
394
+ const createdFile = await fileRepo . create ( {
395
+ id : 1 ,
396
+ title : 'file1' ,
397
+ folderId : 1 ,
398
+ } ) ;
399
+
400
+ fileRepo . registerInclusionResolver ( 'folder' , belongsToResolver ) ;
401
+
402
+ const file = await fileRepo . findById ( 1 , {
403
+ include : [ { relation : 'folder' } ] ,
404
+ } ) ;
405
+
406
+ expect ( file . toJSON ( ) ) . to . deepEqual ( {
407
+ ...createdFile . toJSON ( ) ,
408
+ folder : folders [ 0 ] . toJSON ( ) ,
409
+ } ) ;
410
+ } ) ;
411
+
412
+ it ( 'implements Repository.findOne() with included models' , async ( ) => {
413
+ const folders = await folderRepo . createAll ( [
414
+ { name : 'f1' , id : 1 } ,
415
+ { name : 'f2' , id : 2 } ,
416
+ ] ) ;
417
+ const createdAuthor = await authorRepo . create ( {
418
+ id : 1 ,
419
+ name : 'a1' ,
420
+ folderId : 1 ,
421
+ } ) ;
422
+
423
+ folderRepo . registerInclusionResolver ( 'author' , hasOneResolver ) ;
424
+
425
+ const folder = await folderRepo . findOne ( {
426
+ include : [ { relation : 'author' } ] ,
427
+ } ) ;
428
+
429
+ expect ( folder ! . toJSON ( ) ) . to . deepEqual ( {
430
+ ...folders [ 0 ] . toJSON ( ) ,
431
+ author : createdAuthor . toJSON ( ) ,
432
+ } ) ;
433
+ } ) ;
434
+
435
+ // stub resolvers
436
+
437
+ const hasManyResolver : InclusionResolver < Folder , File > = async entities => {
438
+ const files = [ ] ;
439
+ for ( const entity of entities ) {
440
+ const file = await folderFiles ( entity . id ) . find ( ) ;
441
+ files . push ( file ) ;
442
+ }
443
+
444
+ return files ;
445
+ } ;
446
+
447
+ const belongsToResolver : InclusionResolver <
448
+ File ,
449
+ Folder
450
+ > = async entities => {
451
+ const folders = [ ] ;
452
+
453
+ for ( const file of entities ) {
454
+ const folder = await fileFolder ( file . folderId ) ;
455
+ folders . push ( folder ) ;
456
+ }
457
+
458
+ return folders ;
459
+ } ;
460
+
461
+ const hasOneResolver : InclusionResolver <
462
+ Folder ,
463
+ Author
464
+ > = async entities => {
465
+ const authors = [ ] ;
466
+
467
+ for ( const folder of entities ) {
468
+ const author = await folderAuthor ( folder . id ) . get ( ) ;
469
+ authors . push ( author ) ;
470
+ }
471
+
472
+ return authors ;
473
+ } ;
474
+ } ) ;
475
+
277
476
it ( 'implements Repository.delete()' , async ( ) => {
278
477
const repo = new DefaultCrudRepository ( Note , ds ) ;
279
478
const note = await repo . create ( { title : 't3' , content : 'c3' } ) ;
@@ -419,6 +618,21 @@ describe('DefaultCrudRepository', () => {
419
618
'execute() must be implemented by the connector' ,
420
619
) ;
421
620
} ) ;
621
+
622
+ it ( 'has the property inclusionResolvers' , ( ) => {
623
+ const repo = new DefaultCrudRepository ( Note , ds ) ;
624
+ expect ( repo . inclusionResolvers ) . to . be . instanceof ( Map ) ;
625
+ } ) ;
626
+
627
+ it ( 'implements Repository.registerInclusionResolver()' , ( ) => {
628
+ const repo = new DefaultCrudRepository ( Note , ds ) ;
629
+ const resolver : InclusionResolver < Note , Entity > = async entities => {
630
+ return entities ;
631
+ } ;
632
+ repo . registerInclusionResolver ( 'notes' , resolver ) ;
633
+ const setResolver = repo . inclusionResolvers . get ( 'notes' ) ;
634
+ expect ( setResolver ) . to . eql ( resolver ) ;
635
+ } ) ;
422
636
} ) ;
423
637
424
638
describe ( 'DefaultTransactionalRepository' , ( ) => {
0 commit comments