@squiz/db-lib 1.71.3 → 1.73.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -193,13 +193,11 @@ describe('AbstractRepository', () => {
193
193
  },
194
194
  ConditionExpression: `attribute_exists(pk)`,
195
195
  };
196
- const partialItemWithKeyFields = {
197
- name: 'foo',
198
- };
199
196
  const updateItem = {
197
+ name: 'foo',
200
198
  country: 'au-updated',
201
199
  };
202
- const result = await repository.updateItem(partialItemWithKeyFields, updateItem);
200
+ const result = await repository.updateItem(updateItem);
203
201
  expect(ddbClientMock).toHaveReceivedNthCommandWith(2, lib_dynamodb_1.UpdateCommand, input);
204
202
  expect(result).toEqual(new TestItem({
205
203
  name: 'foo',
@@ -208,19 +206,43 @@ describe('AbstractRepository', () => {
208
206
  data: {},
209
207
  }));
210
208
  });
211
- it('should undefined if item does does not exist', async () => {
209
+ it('should not trigger update request if the input attributes are same as in the existing item', async () => {
212
210
  ddbClientMock.on(lib_dynamodb_1.GetCommand).resolves({
213
211
  $metadata: {
214
212
  httpStatusCode: 200,
215
213
  },
214
+ Item: {
215
+ name: 'foo',
216
+ age: 99,
217
+ country: 'au',
218
+ data: {},
219
+ },
216
220
  });
217
- const partialItemWithKeyFields = {
221
+ ddbClientMock.on(lib_dynamodb_1.UpdateCommand).rejects(new Error('updateItem() called when not expected'));
222
+ // update input attributes are same as in the existing item
223
+ const updateItem = {
218
224
  name: 'foo',
225
+ country: 'au',
219
226
  };
227
+ const result = await repository.updateItem(updateItem);
228
+ expect(result).toEqual(new TestItem({
229
+ name: 'foo',
230
+ age: 99,
231
+ country: 'au',
232
+ data: {},
233
+ }));
234
+ });
235
+ it('should return undefined if item does does not exist', async () => {
236
+ ddbClientMock.on(lib_dynamodb_1.GetCommand).resolves({
237
+ $metadata: {
238
+ httpStatusCode: 200,
239
+ },
240
+ });
220
241
  const updateItem = {
242
+ name: 'foo',
221
243
  country: 'au-updated',
222
244
  };
223
- const result = await repository.updateItem(partialItemWithKeyFields, updateItem);
245
+ const result = await repository.updateItem(updateItem);
224
246
  expect(result).toEqual(undefined);
225
247
  });
226
248
  it('should return undefined if update cmd conditional check fails', async () => {
@@ -239,13 +261,11 @@ describe('AbstractRepository', () => {
239
261
  $metadata: {},
240
262
  message: 'not found',
241
263
  }));
242
- const partialItemWithKeyFields = {
243
- name: 'foo',
244
- };
245
264
  const updateItem = {
265
+ name: 'foo',
246
266
  country: 'au-updated',
247
267
  };
248
- const result = await repository.updateItem(partialItemWithKeyFields, updateItem);
268
+ const result = await repository.updateItem(updateItem);
249
269
  expect(result).toEqual(undefined);
250
270
  });
251
271
  it('should throw error update data has invalid data', async () => {
@@ -260,13 +280,11 @@ describe('AbstractRepository', () => {
260
280
  data: {},
261
281
  },
262
282
  });
263
- const partialItemWithKeyFields = {
264
- name: 'foo',
265
- };
266
283
  const updateItem = {
284
+ name: 'foo',
267
285
  country: 61, // should be "string" type
268
286
  };
269
- await expect(repository.updateItem(partialItemWithKeyFields, updateItem)).rejects.toEqual(new Error('Invalid "country"'));
287
+ await expect(repository.updateItem(updateItem)).rejects.toEqual(new Error('Invalid "country"'));
270
288
  });
271
289
  it('should throw error if excess column in input', async () => {
272
290
  ddbClientMock.on(lib_dynamodb_1.GetCommand).resolves({
@@ -280,23 +298,19 @@ describe('AbstractRepository', () => {
280
298
  data: {},
281
299
  },
282
300
  });
283
- const partialItemWithKeyFields = {
284
- name: 'foo',
285
- };
286
301
  const updateItem = {
302
+ name: 'foo',
287
303
  country: 'au-updated',
288
304
  extra: '',
289
305
  };
290
- await expect(repository.updateItem(partialItemWithKeyFields, updateItem)).rejects.toEqual(new __1.InvalidDbSchemaError('Excess properties in entity test-item-entity: extra'));
306
+ await expect(repository.updateItem(updateItem)).rejects.toEqual(new __1.InvalidDbSchemaError('Excess properties in entity test-item-entity: extra'));
291
307
  });
292
308
  it('should throw error if input does not includes key field(s)', async () => {
293
- const partialItemWithKeyFields = {
294
- age: 99,
295
- };
296
309
  const updateItem = {
310
+ age: 99,
297
311
  country: 'au-updated', // should be "string" type
298
312
  };
299
- await expect(repository.updateItem(partialItemWithKeyFields, updateItem)).rejects.toEqual(new __1.MissingKeyValuesError('Key field "name" must be specified in the input item in entity test-item-entity'));
313
+ await expect(repository.updateItem(updateItem)).rejects.toEqual(new __1.MissingKeyValuesError('Key field "name" must be specified in the input item in entity test-item-entity'));
300
314
  });
301
315
  });
302
316
  describe('getItem()', () => {
@@ -385,6 +399,435 @@ describe('AbstractRepository', () => {
385
399
  await expect(repository.getItem(partialItem)).rejects.toEqual(new InvalidDataFormatError_1.InvalidDataFormatError(`Field 'data2' defined as JSON String has a non-string data`));
386
400
  });
387
401
  });
402
+ describe('getItems()', () => {
403
+ it('should use BatchGetItem to get result', async () => {
404
+ ddbClientMock.on(lib_dynamodb_1.BatchGetCommand).resolves({
405
+ $metadata: {
406
+ httpStatusCode: 200,
407
+ },
408
+ Responses: {
409
+ [TABLE_NAME]: [
410
+ {
411
+ name: 'foo',
412
+ age: 99,
413
+ country: 'au',
414
+ data: {},
415
+ data2: '{"foo":"bar","num":123}',
416
+ },
417
+ {
418
+ name: 'foo2',
419
+ age: 999,
420
+ country: 'au',
421
+ data: {},
422
+ data2: '{"foo":"bar","num":123}',
423
+ },
424
+ ],
425
+ },
426
+ });
427
+ const input = {
428
+ RequestItems: {
429
+ [TABLE_NAME]: {
430
+ Keys: [
431
+ { pk: 'test_item#foo', sk: '#meta' },
432
+ { pk: 'test_item#foo2', sk: '#meta' },
433
+ ],
434
+ },
435
+ },
436
+ };
437
+ const requestItems = [{ name: 'foo' }, { name: 'foo2' }];
438
+ const result = await repository.getItems(requestItems);
439
+ expect(ddbClientMock).toHaveReceivedCommandWith(lib_dynamodb_1.BatchGetCommand, input);
440
+ expect(result).toEqual([
441
+ new TestItem({
442
+ name: 'foo',
443
+ age: 99,
444
+ country: 'au',
445
+ data: {},
446
+ data2: {
447
+ foo: 'bar',
448
+ num: 123,
449
+ },
450
+ }),
451
+ new TestItem({
452
+ name: 'foo2',
453
+ age: 999,
454
+ country: 'au',
455
+ data: {},
456
+ data2: {
457
+ foo: 'bar',
458
+ num: 123,
459
+ },
460
+ }),
461
+ ]);
462
+ });
463
+ it('should retry if unprocessed keys returned', async () => {
464
+ const input1 = {
465
+ RequestItems: {
466
+ [TABLE_NAME]: {
467
+ Keys: [
468
+ { pk: 'test_item#foo', sk: '#meta' },
469
+ { pk: 'test_item#foo2', sk: '#meta' },
470
+ ],
471
+ },
472
+ },
473
+ };
474
+ const input2 = {
475
+ RequestItems: {
476
+ [TABLE_NAME]: {
477
+ Keys: [{ pk: 'test_item#foo2', sk: '#meta' }],
478
+ },
479
+ },
480
+ };
481
+ ddbClientMock.on(lib_dynamodb_1.BatchGetCommand, input1).resolves({
482
+ $metadata: {
483
+ httpStatusCode: 200,
484
+ },
485
+ Responses: {
486
+ [TABLE_NAME]: [
487
+ {
488
+ name: 'foo',
489
+ age: 99,
490
+ country: 'au',
491
+ data: {},
492
+ data2: '{"foo":"bar","num":123}',
493
+ },
494
+ ],
495
+ },
496
+ UnprocessedKeys: {
497
+ [TABLE_NAME]: {
498
+ Keys: [{ pk: 'test_item#foo2', sk: '#meta' }],
499
+ },
500
+ },
501
+ });
502
+ ddbClientMock.on(lib_dynamodb_1.BatchGetCommand, input2).resolves({
503
+ $metadata: {
504
+ httpStatusCode: 200,
505
+ },
506
+ Responses: {
507
+ [TABLE_NAME]: [
508
+ {
509
+ name: 'foo2',
510
+ age: 999,
511
+ country: 'au',
512
+ data: {},
513
+ data2: '{"foo":"bar","num":123}',
514
+ },
515
+ ],
516
+ },
517
+ UnprocessedKeys: {},
518
+ });
519
+ const requestItems = [{ name: 'foo' }, { name: 'foo2' }];
520
+ const result = await repository.getItems(requestItems);
521
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(1, lib_dynamodb_1.BatchGetCommand, input1);
522
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(2, lib_dynamodb_1.BatchGetCommand, input2);
523
+ expect(result).toEqual([
524
+ new TestItem({
525
+ name: 'foo',
526
+ age: 99,
527
+ country: 'au',
528
+ data: {},
529
+ data2: {
530
+ foo: 'bar',
531
+ num: 123,
532
+ },
533
+ }),
534
+ new TestItem({
535
+ name: 'foo2',
536
+ age: 999,
537
+ country: 'au',
538
+ data: {},
539
+ data2: {
540
+ foo: 'bar',
541
+ num: 123,
542
+ },
543
+ }),
544
+ ]);
545
+ });
546
+ it('should fail after max retries for unprocessed keys', async () => {
547
+ const input1 = {
548
+ RequestItems: {
549
+ [TABLE_NAME]: {
550
+ Keys: [
551
+ { pk: 'test_item#foo', sk: '#meta' },
552
+ { pk: 'test_item#foo2', sk: '#meta' },
553
+ ],
554
+ },
555
+ },
556
+ };
557
+ const input2 = {
558
+ RequestItems: {
559
+ [TABLE_NAME]: {
560
+ Keys: [{ pk: 'test_item#foo2', sk: '#meta' }],
561
+ },
562
+ },
563
+ };
564
+ ddbClientMock.on(lib_dynamodb_1.BatchGetCommand, input1).resolves({
565
+ $metadata: {
566
+ httpStatusCode: 200,
567
+ },
568
+ Responses: {
569
+ [TABLE_NAME]: [
570
+ {
571
+ name: 'foo',
572
+ age: 99,
573
+ country: 'au',
574
+ data: {},
575
+ data2: '{"foo":"bar","num":123}',
576
+ },
577
+ ],
578
+ },
579
+ UnprocessedKeys: {
580
+ [TABLE_NAME]: {
581
+ Keys: [{ pk: 'test_item#foo2', sk: '#meta' }],
582
+ },
583
+ },
584
+ });
585
+ ddbClientMock.on(lib_dynamodb_1.BatchGetCommand, input2).resolves({
586
+ $metadata: {
587
+ httpStatusCode: 200,
588
+ },
589
+ Responses: {},
590
+ UnprocessedKeys: {
591
+ [TABLE_NAME]: {
592
+ Keys: [{ pk: 'test_item#foo2', sk: '#meta' }],
593
+ },
594
+ },
595
+ });
596
+ const requestItems = [{ name: 'foo' }, { name: 'foo2' }];
597
+ await expect(repository.getItems(requestItems)).rejects.toEqual(new Error('Maximum allowed retries exceeded for unprocessed items'));
598
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(1, lib_dynamodb_1.BatchGetCommand, input1);
599
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(2, lib_dynamodb_1.BatchGetCommand, input2);
600
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(3, lib_dynamodb_1.BatchGetCommand, input2);
601
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(4, lib_dynamodb_1.BatchGetCommand, input2);
602
+ });
603
+ it('should request BatchGetItem in batch of 100 items to get result', async () => {
604
+ ddbClientMock.on(lib_dynamodb_1.BatchGetCommand).resolves({
605
+ $metadata: {
606
+ httpStatusCode: 200,
607
+ },
608
+ });
609
+ const requestItems = [];
610
+ for (let i = 0; i < 120; i++) {
611
+ requestItems.push({ name: `foo${i}` });
612
+ }
613
+ // keys for first batch request
614
+ const keys1 = [];
615
+ for (let i = 0; i < 100; i++) {
616
+ keys1.push({ pk: `test_item#foo${i}`, sk: '#meta' });
617
+ }
618
+ // keys for second batch request
619
+ const keys2 = [];
620
+ for (let i = 100; i < 120; i++) {
621
+ keys2.push({ pk: `test_item#foo${i}`, sk: '#meta' });
622
+ }
623
+ const input1 = {
624
+ RequestItems: {
625
+ [TABLE_NAME]: {
626
+ Keys: keys1,
627
+ },
628
+ },
629
+ };
630
+ const input2 = {
631
+ RequestItems: {
632
+ [TABLE_NAME]: {
633
+ Keys: keys2,
634
+ },
635
+ },
636
+ };
637
+ await repository.getItems(requestItems);
638
+ expect(ddbClientMock).toHaveReceivedCommandTimes(lib_dynamodb_1.BatchGetCommand, 2);
639
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(1, lib_dynamodb_1.BatchGetCommand, input1);
640
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(2, lib_dynamodb_1.BatchGetCommand, input2);
641
+ });
642
+ it('should throw error if any input item does not includes key field(s)', async () => {
643
+ const requestItems = [{ name: 'foo' }, { age: 22 }];
644
+ await expect(repository.getItems(requestItems)).rejects.toEqual(new __1.MissingKeyValuesError('Key field "name" must be specified in the input item in entity test-item-entity'));
645
+ });
646
+ });
647
+ describe('deleteItems()', () => {
648
+ it('should use batchWrite() to get result', async () => {
649
+ ddbClientMock.on(lib_dynamodb_1.BatchWriteCommand).resolves({
650
+ $metadata: {
651
+ httpStatusCode: 200,
652
+ },
653
+ ItemCollectionMetrics: {
654
+ [TABLE_NAME]: [{}],
655
+ },
656
+ });
657
+ const input = {
658
+ RequestItems: {
659
+ [TABLE_NAME]: [
660
+ {
661
+ DeleteRequest: {
662
+ Key: { pk: 'test_item#foo', sk: '#meta' },
663
+ },
664
+ },
665
+ {
666
+ DeleteRequest: {
667
+ Key: { pk: 'test_item#foo2', sk: '#meta' },
668
+ },
669
+ },
670
+ ],
671
+ },
672
+ };
673
+ const requestItems = [{ name: 'foo' }, { name: 'foo2' }];
674
+ await repository.deleteItems(requestItems);
675
+ expect(ddbClientMock).toHaveReceivedCommandWith(lib_dynamodb_1.BatchWriteCommand, input);
676
+ });
677
+ it('should use re-try if unprocessed items returned', async () => {
678
+ const input1 = {
679
+ RequestItems: {
680
+ [TABLE_NAME]: [
681
+ {
682
+ DeleteRequest: {
683
+ Key: { pk: 'test_item#foo', sk: '#meta' },
684
+ },
685
+ },
686
+ {
687
+ DeleteRequest: {
688
+ Key: { pk: 'test_item#foo2', sk: '#meta' },
689
+ },
690
+ },
691
+ ],
692
+ },
693
+ };
694
+ const input2 = {
695
+ RequestItems: {
696
+ [TABLE_NAME]: [
697
+ {
698
+ DeleteRequest: {
699
+ Key: { pk: 'test_item#foo2', sk: '#meta' },
700
+ },
701
+ },
702
+ ],
703
+ },
704
+ };
705
+ ddbClientMock.on(lib_dynamodb_1.BatchWriteCommand, input1).resolves({
706
+ $metadata: {
707
+ httpStatusCode: 200,
708
+ },
709
+ UnprocessedItems: {
710
+ [TABLE_NAME]: [
711
+ {
712
+ DeleteRequest: {
713
+ Key: { pk: 'test_item#foo2', sk: '#meta' },
714
+ },
715
+ },
716
+ ],
717
+ },
718
+ });
719
+ ddbClientMock.on(lib_dynamodb_1.BatchWriteCommand, input2).resolves({
720
+ $metadata: {
721
+ httpStatusCode: 200,
722
+ },
723
+ UnprocessedItems: {},
724
+ });
725
+ const requestItems = [{ name: 'foo' }, { name: 'foo2' }];
726
+ await repository.deleteItems(requestItems);
727
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(1, lib_dynamodb_1.BatchWriteCommand, input1);
728
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(2, lib_dynamodb_1.BatchWriteCommand, input2);
729
+ });
730
+ it('should fail after max number of retries', async () => {
731
+ const input1 = {
732
+ RequestItems: {
733
+ [TABLE_NAME]: [
734
+ {
735
+ DeleteRequest: {
736
+ Key: { pk: 'test_item#foo', sk: '#meta' },
737
+ },
738
+ },
739
+ {
740
+ DeleteRequest: {
741
+ Key: { pk: 'test_item#foo2', sk: '#meta' },
742
+ },
743
+ },
744
+ ],
745
+ },
746
+ };
747
+ const input2 = {
748
+ RequestItems: {
749
+ [TABLE_NAME]: [
750
+ {
751
+ DeleteRequest: {
752
+ Key: { pk: 'test_item#foo2', sk: '#meta' },
753
+ },
754
+ },
755
+ ],
756
+ },
757
+ };
758
+ ddbClientMock.on(lib_dynamodb_1.BatchWriteCommand).resolves({
759
+ $metadata: {
760
+ httpStatusCode: 200,
761
+ },
762
+ UnprocessedItems: {
763
+ [TABLE_NAME]: [
764
+ {
765
+ DeleteRequest: {
766
+ Key: { pk: 'test_item#foo2', sk: '#meta' },
767
+ },
768
+ },
769
+ ],
770
+ },
771
+ });
772
+ const requestItems = [{ name: 'foo' }, { name: 'foo2' }];
773
+ await expect(repository.deleteItems(requestItems)).rejects.toEqual(new Error('Maximum allowed retries exceeded for unprocessed items'));
774
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(1, lib_dynamodb_1.BatchWriteCommand, input1);
775
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(2, lib_dynamodb_1.BatchWriteCommand, input2);
776
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(3, lib_dynamodb_1.BatchWriteCommand, input2);
777
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(4, lib_dynamodb_1.BatchWriteCommand, input2);
778
+ });
779
+ it('should request batchWrite in batch of 25 items to get result', async () => {
780
+ ddbClientMock.on(lib_dynamodb_1.BatchWriteCommand).resolves({
781
+ $metadata: {
782
+ httpStatusCode: 200,
783
+ },
784
+ });
785
+ const requestItems = [];
786
+ for (let i = 0; i < 30; i++) {
787
+ requestItems.push({ name: `foo${i}` });
788
+ }
789
+ // keys for first batch request
790
+ const keys1 = [];
791
+ for (let i = 0; i < 25; i++) {
792
+ keys1.push({ pk: `test_item#foo${i}`, sk: '#meta' });
793
+ }
794
+ // keys for second batch request
795
+ const keys2 = [];
796
+ for (let i = 25; i < 30; i++) {
797
+ keys2.push({ pk: `test_item#foo${i}`, sk: '#meta' });
798
+ }
799
+ const input1 = {
800
+ RequestItems: {
801
+ [TABLE_NAME]: keys1.map((key) => {
802
+ return {
803
+ DeleteRequest: {
804
+ Key: key,
805
+ },
806
+ };
807
+ }),
808
+ },
809
+ };
810
+ const input2 = {
811
+ RequestItems: {
812
+ [TABLE_NAME]: keys2.map((key) => {
813
+ return {
814
+ DeleteRequest: {
815
+ Key: key,
816
+ },
817
+ };
818
+ }),
819
+ },
820
+ };
821
+ await repository.deleteItems(requestItems);
822
+ expect(ddbClientMock).toHaveReceivedCommandTimes(lib_dynamodb_1.BatchWriteCommand, 2);
823
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(1, lib_dynamodb_1.BatchWriteCommand, input1);
824
+ expect(ddbClientMock).toHaveReceivedNthCommandWith(2, lib_dynamodb_1.BatchWriteCommand, input2);
825
+ });
826
+ it('should throw error if any input item does not includes key field(s)', async () => {
827
+ const requestItems = [{ name: 'foo' }, { age: 22 }];
828
+ await expect(repository.deleteItems(requestItems)).rejects.toEqual(new __1.MissingKeyValuesError('Key field "name" must be specified in the input item in entity test-item-entity'));
829
+ });
830
+ });
388
831
  describe('queryItems()', () => {
389
832
  it('should return the items if found', async () => {
390
833
  ddbClientMock.on(lib_dynamodb_1.QueryCommand).resolves({
@@ -462,8 +905,7 @@ describe('AbstractRepository', () => {
462
905
  },
463
906
  };
464
907
  const partialItem = { name: 'foo' };
465
- const useSortKey = true;
466
- const _result = await repository.queryItems(partialItem, useSortKey);
908
+ const _result = await repository.queryItems(partialItem, { useSortKey: true });
467
909
  expect(ddbClientMock).toHaveReceivedCommandWith(lib_dynamodb_1.QueryCommand, input);
468
910
  });
469
911
  it('should return the items if found when using gsi index', async () => {
@@ -500,7 +942,7 @@ describe('AbstractRepository', () => {
500
942
  };
501
943
  const partialItem = { country: 'au' };
502
944
  const useSortKey = false;
503
- const result = await repository.queryItems(partialItem, useSortKey, index);
945
+ const result = await repository.queryItems(partialItem, { useSortKey, index });
504
946
  expect(ddbClientMock).toHaveReceivedCommandWith(lib_dynamodb_1.QueryCommand, input);
505
947
  expect(result).toEqual([
506
948
  new TestItem({
@@ -539,13 +981,122 @@ describe('AbstractRepository', () => {
539
981
  };
540
982
  const partialItem = { age: 99, country: 'au' };
541
983
  const useSortKey = true;
542
- const _result = await repository.queryItems(partialItem, useSortKey, index);
984
+ const _result = await repository.queryItems(partialItem, { useSortKey, index });
985
+ expect(ddbClientMock).toHaveReceivedCommandWith(lib_dynamodb_1.QueryCommand, input);
986
+ });
987
+ it('should set input query correctly when "filter - begins_with" query option is set', async () => {
988
+ ddbClientMock.on(lib_dynamodb_1.QueryCommand).resolves({
989
+ $metadata: {
990
+ httpStatusCode: 200,
991
+ },
992
+ });
993
+ const input = {
994
+ TableName: TABLE_NAME,
995
+ KeyConditionExpression: '#pkName = :pkValue AND begins_with(#skName, :skValue)',
996
+ ExpressionAttributeNames: {
997
+ '#pkName': 'pk',
998
+ '#skName': 'sk',
999
+ },
1000
+ ExpressionAttributeValues: {
1001
+ ':pkValue': 'test_item#foo',
1002
+ ':skValue': 'keyword-x',
1003
+ },
1004
+ };
1005
+ const partialItem = { name: 'foo' };
1006
+ const queryOptions = {
1007
+ filter: {
1008
+ type: 'begins_with',
1009
+ keyword: 'keyword-x',
1010
+ },
1011
+ };
1012
+ await repository.queryItems(partialItem, queryOptions);
1013
+ expect(ddbClientMock).toHaveReceivedCommandWith(lib_dynamodb_1.QueryCommand, input);
1014
+ });
1015
+ it('should throw error invalid "filter" query option is set', async () => {
1016
+ ddbClientMock.on(lib_dynamodb_1.QueryCommand).resolves({
1017
+ $metadata: {
1018
+ httpStatusCode: 200,
1019
+ },
1020
+ });
1021
+ const partialItem = { name: 'foo' };
1022
+ const queryOptions = {
1023
+ filter: {
1024
+ type: 'invalid-type',
1025
+ keyword: 'keyword-x',
1026
+ },
1027
+ };
1028
+ await expect(repository.queryItems(partialItem, queryOptions)).rejects.toEqual(new Error(`Invalid query filter type: invalid-type`));
1029
+ });
1030
+ it('should set input query correctly when "limit" query option is set', async () => {
1031
+ ddbClientMock.on(lib_dynamodb_1.QueryCommand).resolves({
1032
+ $metadata: {
1033
+ httpStatusCode: 200,
1034
+ },
1035
+ });
1036
+ const input = {
1037
+ TableName: TABLE_NAME,
1038
+ KeyConditionExpression: '#pkName = :pkValue',
1039
+ ExpressionAttributeNames: {
1040
+ '#pkName': 'pk',
1041
+ },
1042
+ ExpressionAttributeValues: {
1043
+ ':pkValue': 'test_item#foo',
1044
+ },
1045
+ Limit: 33,
1046
+ };
1047
+ const partialItem = { name: 'foo' };
1048
+ const queryOptions = { limit: 33 };
1049
+ await repository.queryItems(partialItem, queryOptions);
1050
+ expect(ddbClientMock).toHaveReceivedCommandWith(lib_dynamodb_1.QueryCommand, input);
1051
+ });
1052
+ it('should set input query correctly when "order asc" query option is set', async () => {
1053
+ ddbClientMock.on(lib_dynamodb_1.QueryCommand).resolves({
1054
+ $metadata: {
1055
+ httpStatusCode: 200,
1056
+ },
1057
+ });
1058
+ const input = {
1059
+ TableName: TABLE_NAME,
1060
+ KeyConditionExpression: '#pkName = :pkValue',
1061
+ ExpressionAttributeNames: {
1062
+ '#pkName': 'pk',
1063
+ },
1064
+ ExpressionAttributeValues: {
1065
+ ':pkValue': 'test_item#foo',
1066
+ },
1067
+ ScanIndexForward: true,
1068
+ };
1069
+ const partialItem = { name: 'foo' };
1070
+ const queryOptions = { order: 'asc' };
1071
+ await repository.queryItems(partialItem, queryOptions);
1072
+ expect(ddbClientMock).toHaveReceivedCommandWith(lib_dynamodb_1.QueryCommand, input);
1073
+ });
1074
+ it('should set input query correctly when "order desc" query option is set', async () => {
1075
+ ddbClientMock.on(lib_dynamodb_1.QueryCommand).resolves({
1076
+ $metadata: {
1077
+ httpStatusCode: 200,
1078
+ },
1079
+ });
1080
+ const input = {
1081
+ TableName: TABLE_NAME,
1082
+ KeyConditionExpression: '#pkName = :pkValue',
1083
+ ExpressionAttributeNames: {
1084
+ '#pkName': 'pk',
1085
+ },
1086
+ ExpressionAttributeValues: {
1087
+ ':pkValue': 'test_item#foo',
1088
+ },
1089
+ ScanIndexForward: false,
1090
+ };
1091
+ const partialItem = { name: 'foo' };
1092
+ const queryOptions = { order: 'desc' };
1093
+ await repository.queryItems(partialItem, queryOptions);
543
1094
  expect(ddbClientMock).toHaveReceivedCommandWith(lib_dynamodb_1.QueryCommand, input);
544
1095
  });
545
1096
  it('should throw error for invalid index query', async () => {
546
1097
  const index = 'undefined-index';
547
1098
  const partialItem = { age: 99, country: 'au' };
548
- await expect(repository.queryItems(partialItem, false, index)).rejects.toEqual(new __1.MissingKeyValuesError(`Table index '${index}' not defined on entity test-item-entity`));
1099
+ await expect(repository.queryItems(partialItem, { index })).rejects.toEqual(new __1.MissingKeyValuesError(`Table index '${index}' not defined on entity test-item-entity`));
549
1100
  });
550
1101
  it('should throw error for missing key fields', async () => {
551
1102
  const partialItem = { age: 99, country: 'au' };
@@ -555,13 +1106,13 @@ describe('AbstractRepository', () => {
555
1106
  const partialItem = { name: 'foo' };
556
1107
  const useSortKey = false;
557
1108
  const index = 'gsi1_pk-gsi1_sk-index';
558
- await expect(repository.queryItems(partialItem, useSortKey, index)).rejects.toEqual(new __1.MissingKeyValuesError(`Key field "country" must be specified in the input item in entity test-item-entity`));
1109
+ await expect(repository.queryItems(partialItem, { useSortKey, index })).rejects.toEqual(new __1.MissingKeyValuesError(`Key field "country" must be specified in the input item in entity test-item-entity`));
559
1110
  });
560
1111
  it('should throw error for missing key fields with "useSortKey" param true when using index', async () => {
561
1112
  const partialItem = { country: 'au' };
562
1113
  const useSortKey = true;
563
1114
  const index = 'gsi1_pk-gsi1_sk-index';
564
- await expect(repository.queryItems(partialItem, useSortKey, index)).rejects.toEqual(new __1.MissingKeyValuesError(`Key field "age" must be specified in the input item in entity test-item-entity`));
1115
+ await expect(repository.queryItems(partialItem, { useSortKey, index })).rejects.toEqual(new __1.MissingKeyValuesError(`Key field "age" must be specified in the input item in entity test-item-entity`));
565
1116
  });
566
1117
  });
567
1118
  describe('deleteItem()', () => {
@@ -628,7 +1179,6 @@ describe('AbstractRepository', () => {
628
1179
  await repository.deleteItem({ name: 'foo' }, transaction);
629
1180
  await repository.updateItem({
630
1181
  name: 'foo2',
631
- }, {
632
1182
  age: 55,
633
1183
  }, transaction);
634
1184
  return await repository.createItem({