@@ -13,7 +13,7 @@ import {
13
13
UpdateExpression ,
14
14
} from "@aws/dynamodb-expressions" ;
15
15
import { ItemNotFoundException } from "./ItemNotFoundException" ;
16
- import { BatchGetOptions , ParallelScanState } from './index' ;
16
+ import { BatchGetOptions , ParallelScanState , GlobalSecondaryIndexOptions } from './index' ;
17
17
18
18
type BinaryValue = ArrayBuffer | ArrayBufferView ;
19
19
@@ -664,6 +664,105 @@ describe('DataMapper', () => {
664
664
}
665
665
} ) ;
666
666
667
+ describe ( '#createGlobalSecondaryIndex' , ( ) => {
668
+ const waitPromiseFunc = jest . fn ( ( ) => Promise . resolve ( ) ) ;
669
+ const updateTablePromiseFunc = jest . fn ( ( ) => Promise . resolve ( { } ) ) ;
670
+ const mockDynamoDbClient = {
671
+ config : { } ,
672
+ updateTable : jest . fn ( ( ) => ( { promise : updateTablePromiseFunc } ) ) ,
673
+ waitFor : jest . fn ( ( ) => ( { promise : waitPromiseFunc } ) ) ,
674
+ } ;
675
+
676
+ beforeEach ( ( ) => {
677
+ updateTablePromiseFunc . mockClear ( ) ;
678
+ mockDynamoDbClient . updateTable . mockClear ( ) ;
679
+ waitPromiseFunc . mockClear ( ) ;
680
+ mockDynamoDbClient . waitFor . mockClear ( ) ;
681
+ } ) ;
682
+
683
+ const mapper = new DataMapper ( {
684
+ client : mockDynamoDbClient as any ,
685
+ } ) ;
686
+
687
+ class Item {
688
+ get [ DynamoDbTable ] ( ) { return 'foo' }
689
+
690
+ get [ DynamoDbSchema ] ( ) {
691
+ return {
692
+ id : {
693
+ type : 'String' ,
694
+ keyType : 'HASH'
695
+ } ,
696
+ description : {
697
+ type : 'String' ,
698
+ indexKeyConfigurations : {
699
+ DescriptionIndex : 'HASH'
700
+ }
701
+ }
702
+ } ;
703
+ }
704
+ }
705
+
706
+ const DescriptionIndex : GlobalSecondaryIndexOptions = {
707
+ projection : 'all' ,
708
+ readCapacityUnits : 1 ,
709
+ type : 'global' ,
710
+ writeCapacityUnits : 1
711
+ } ;
712
+
713
+ it ( 'should make and send an UpdateTable request' , async ( ) => {
714
+ await mapper . createGlobalSecondaryIndex ( Item , 'DescriptionIndex' , {
715
+ indexOptions : {
716
+ DescriptionIndex
717
+ } ,
718
+ readCapacityUnits : 5 ,
719
+ writeCapacityUnits : 5 ,
720
+ } ) ;
721
+
722
+ expect ( mockDynamoDbClient . updateTable . mock . calls ) . toEqual ( [
723
+ [
724
+ {
725
+ TableName : 'foo' ,
726
+ AttributeDefinitions : [
727
+ {
728
+ AttributeName : 'id' ,
729
+ AttributeType : 'S'
730
+ } ,
731
+ {
732
+ AttributeName : 'description' ,
733
+ AttributeType : 'S'
734
+ }
735
+ ] ,
736
+ GlobalSecondaryIndexUpdates : [
737
+ {
738
+ Create : {
739
+ IndexName : 'DescriptionIndex' ,
740
+ KeySchema : [
741
+ {
742
+ AttributeName : 'description' ,
743
+ KeyType : 'HASH'
744
+ }
745
+ ] ,
746
+ Projection : {
747
+ ProjectionType : 'ALL'
748
+ } ,
749
+ ProvisionedThroughput : {
750
+ ReadCapacityUnits : 1 ,
751
+ WriteCapacityUnits : 1
752
+ }
753
+ }
754
+ }
755
+ ] ,
756
+ } ,
757
+ ]
758
+ ] ) ;
759
+
760
+ expect ( mockDynamoDbClient . waitFor . mock . calls ) . toEqual ( [
761
+ [ 'tableExists' , { TableName : 'foo' } ] ,
762
+ ] ) ;
763
+ } ) ;
764
+ } )
765
+
667
766
describe ( '#createTable' , ( ) => {
668
767
const waitPromiseFunc = jest . fn ( ( ) => Promise . resolve ( ) ) ;
669
768
const createTablePromiseFunc = jest . fn ( ( ) => Promise . resolve ( { } ) ) ;
@@ -1487,6 +1586,135 @@ describe('DataMapper', () => {
1487
1586
} ) ;
1488
1587
} ) ;
1489
1588
1589
+
1590
+ describe ( '#ensureGlobalSecondaryIndexExists' , ( ) => {
1591
+ const waitPromiseFunc = jest . fn ( ( ) => Promise . resolve ( ) ) ;
1592
+ const describeTablePromiseFunc = jest . fn ( ( ) => Promise . resolve ( {
1593
+ Table : {
1594
+ TableStatus : 'ACTIVE' ,
1595
+ GlobalSecondaryIndexes : [
1596
+ {
1597
+ IndexName : 'DescriptionIndex'
1598
+ }
1599
+ ] ,
1600
+ }
1601
+ } ) ) ;
1602
+ const mockDynamoDbClient = {
1603
+ config : { } ,
1604
+ describeTable : jest . fn ( ( ) => ( { promise : describeTablePromiseFunc } ) ) ,
1605
+ waitFor : jest . fn ( ( ) => ( { promise : waitPromiseFunc } ) ) ,
1606
+ } ;
1607
+
1608
+ const mapper = new DataMapper ( {
1609
+ client : mockDynamoDbClient as any ,
1610
+ } ) ;
1611
+ mapper . createGlobalSecondaryIndex = jest . fn ( ( ) => Promise . resolve ( ) ) ;
1612
+
1613
+ beforeEach ( ( ) => {
1614
+ ( mapper . createGlobalSecondaryIndex as any ) . mockClear ( ) ;
1615
+ mockDynamoDbClient . describeTable . mockClear ( ) ;
1616
+ waitPromiseFunc . mockClear ( ) ;
1617
+ mockDynamoDbClient . waitFor . mockClear ( ) ;
1618
+ } ) ;
1619
+
1620
+ let tableName = 'foo' ;
1621
+ let schema = {
1622
+ id : {
1623
+ type : 'String' ,
1624
+ keyType : 'HASH'
1625
+ } ,
1626
+ description : {
1627
+ type : 'String' ,
1628
+ indexKeyConfigurations : {
1629
+ DescriptionIndex : 'HASH'
1630
+ }
1631
+ }
1632
+ } ;
1633
+
1634
+ class Item {
1635
+ get [ DynamoDbTable ] ( ) { return tableName }
1636
+
1637
+ get [ DynamoDbSchema ] ( ) { return schema ; }
1638
+ }
1639
+
1640
+ const DescriptionIndex : GlobalSecondaryIndexOptions = {
1641
+ projection : 'all' ,
1642
+ readCapacityUnits : 1 ,
1643
+ type : 'global' ,
1644
+ writeCapacityUnits : 1
1645
+ } ;
1646
+
1647
+ it (
1648
+ 'should resolve immediately if the table exists, is active, and the GSI already exists' ,
1649
+ async ( ) => {
1650
+ await mapper . ensureGlobalSecondaryIndexExists ( Item , 'DescriptionIndex' , {
1651
+ indexOptions : {
1652
+ DescriptionIndex
1653
+ } ,
1654
+ readCapacityUnits : 5 ,
1655
+ writeCapacityUnits : 5 ,
1656
+ } ) ;
1657
+
1658
+ expect ( mockDynamoDbClient . describeTable . mock . calls ) . toEqual ( [
1659
+ [ { TableName : tableName } ]
1660
+ ] ) ;
1661
+
1662
+ expect ( mockDynamoDbClient . waitFor . mock . calls . length ) . toBe ( 0 ) ;
1663
+ expect ( ( mapper . createGlobalSecondaryIndex as any ) . mock . calls . length ) . toBe ( 0 ) ;
1664
+ }
1665
+ ) ;
1666
+
1667
+ it (
1668
+ 'should attempt to create the index if the table exists in the ACTIVE state but the specified index does not exist' ,
1669
+ async ( ) => {
1670
+ describeTablePromiseFunc . mockImplementationOnce ( ( ) => Promise . resolve ( {
1671
+ Table : { TableStatus : 'ACTIVE' }
1672
+ } ) )
1673
+ await mapper . ensureGlobalSecondaryIndexExists ( Item , 'DescriptionIndex' , {
1674
+ indexOptions : {
1675
+ DescriptionIndex
1676
+ } ,
1677
+ readCapacityUnits : 5 ,
1678
+ writeCapacityUnits : 5 ,
1679
+ } ) ;
1680
+
1681
+ expect ( mockDynamoDbClient . describeTable . mock . calls ) . toEqual ( [
1682
+ [ { TableName : tableName } ]
1683
+ ] ) ;
1684
+
1685
+ expect ( ( mapper . createGlobalSecondaryIndex as any ) . mock . calls . length ) . toBe ( 1 ) ;
1686
+ expect ( mockDynamoDbClient . waitFor . mock . calls . length ) . toBe ( 0 ) ;
1687
+ }
1688
+ ) ;
1689
+
1690
+ it (
1691
+ 'should rethrow if "describeTable" throws a "ResourceNotFoundException"' ,
1692
+ async ( ) => {
1693
+ const expectedError = new Error ( 'No such table!' ) ;
1694
+ expectedError . name = 'ResourceNotFoundException' ;
1695
+ describeTablePromiseFunc . mockImplementationOnce ( async ( ) => {
1696
+ throw expectedError ;
1697
+ } ) ;
1698
+
1699
+ await expect ( mapper . ensureGlobalSecondaryIndexExists ( Item , 'DescriptionIndex' , {
1700
+ indexOptions : {
1701
+ DescriptionIndex
1702
+ } ,
1703
+ readCapacityUnits : 5 ,
1704
+ writeCapacityUnits : 5 ,
1705
+ } ) )
1706
+ . rejects
1707
+ . toMatchObject ( expectedError ) ;
1708
+
1709
+ expect ( mockDynamoDbClient . describeTable . mock . calls ) . toEqual ( [
1710
+ [ { TableName : tableName } ]
1711
+ ] ) ;
1712
+
1713
+ expect ( mockDynamoDbClient . waitFor . mock . calls . length ) . toBe ( 0 ) ;
1714
+ }
1715
+ ) ;
1716
+ } ) ;
1717
+
1490
1718
describe ( '#ensureTableExists' , ( ) => {
1491
1719
const waitPromiseFunc = jest . fn ( ( ) => Promise . resolve ( ) ) ;
1492
1720
const describeTablePromiseFunc = jest . fn ( ( ) => Promise . resolve ( {
0 commit comments