1+ import * as AWS from 'aws-sdk' ;
12import { DocumentClient } from 'aws-sdk/clients/dynamodb' ;
23import { sort } from 'ramda' ;
34import { Storage } from 'umzug' ;
45
6+ const RESOURCE_NOT_FOUND_EXCEPTION = 'ResourceNotFoundException' ;
57interface DynamoDBStorageOptions {
6- dynamodb ?: DocumentClient ;
8+ dynamodb ?: AWS . DynamoDB ;
9+ documentClient ?: DocumentClient ;
710 tableName ?: string ;
811 attributeName ?: string ;
912 timestamp ?: boolean ;
@@ -13,7 +16,8 @@ interface DynamoDBStorageOptions {
1316 * @class DynamoDBStorage
1417 */
1518export default class DynamoDBStorage implements Storage {
16- private dynamodb : DocumentClient ;
19+ private dynamodb : AWS . DynamoDB ;
20+ private documentClient : DocumentClient ;
1721 private tableName : string ;
1822 private attributeName : string ;
1923 private timestamp : boolean ;
@@ -22,16 +26,21 @@ export default class DynamoDBStorage implements Storage {
2226 * Constructs DynamoDB table storage.
2327 *
2428 * @param options
25- * @param options.dynamodb - a DynamoDB document client instance
29+ * @param options.dynamodb - a DynamoDB instance
30+ * @param options.documentClient - a DynamoDB document client instance
2631 * @param options.tableName - name of migration table in DynamoDB
2732 * @param options.attributeName - name of the table primaryKey attribute in DynamoDB
2833 * @param options.timestamp - option to add timestamps to the DynamoDB table
2934 */
30- constructor ( { dynamodb, tableName, attributeName, timestamp } : DynamoDBStorageOptions = { } ) {
31- if ( dynamodb && ! ( dynamodb instanceof DocumentClient ) ) {
32- throw new Error ( '"dynamodb" must be a DocumentClient instance' ) ;
35+ constructor ( { dynamodb, documentClient , tableName, attributeName, timestamp } : DynamoDBStorageOptions = { } ) {
36+ if ( dynamodb && ! ( dynamodb instanceof AWS . DynamoDB ) ) {
37+ throw new Error ( '"dynamodb" must be a AWS.DynamoDB instance' ) ;
3338 }
34- this . dynamodb = dynamodb || new DocumentClient ( ) ;
39+ if ( documentClient && ! ( documentClient instanceof DocumentClient ) ) {
40+ throw new Error ( '"documentClient" must be a DocumentClient instance' ) ;
41+ }
42+ this . dynamodb = dynamodb || new AWS . DynamoDB ( ) ;
43+ this . documentClient = documentClient || new DocumentClient ( { service : this . dynamodb } ) ;
3544 this . tableName = tableName || 'migrations' ;
3645 this . attributeName = attributeName || 'name' ;
3746 this . timestamp = timestamp || false ;
@@ -49,7 +58,7 @@ export default class DynamoDBStorage implements Storage {
4958 item . createdAt = Date . now ( ) ;
5059 }
5160
52- await this . dynamodb . put ( { TableName : this . tableName , Item : item } ) . promise ( ) ;
61+ await this . documentClient . put ( { TableName : this . tableName , Item : item } ) . promise ( ) ;
5362 }
5463
5564 /**
@@ -60,7 +69,7 @@ export default class DynamoDBStorage implements Storage {
6069 async unlogMigration ( migrationName : string ) {
6170 const key : DocumentClient . Key = { [ this . attributeName ] : migrationName } ;
6271
63- await this . dynamodb . delete ( { TableName : this . tableName , Key : key } ) . promise ( ) ;
72+ await this . documentClient . delete ( { TableName : this . tableName , Key : key } ) . promise ( ) ;
6473 }
6574
6675 /**
@@ -71,10 +80,23 @@ export default class DynamoDBStorage implements Storage {
7180 let startKey : DocumentClient . Key ;
7281
7382 do {
74- const { Items, LastEvaluatedKey } = await this . dynamodb . scan ( {
83+ const { Items, LastEvaluatedKey } = await this . documentClient . scan ( {
7584 TableName : this . tableName ,
7685 ExclusiveStartKey : startKey ,
77- } ) . promise ( ) ;
86+ } )
87+ . promise ( )
88+ . catch ( error => {
89+ if ( error === RESOURCE_NOT_FOUND_EXCEPTION ) {
90+ return this . createMigrationTable ( )
91+ . then ( ( ) => ( {
92+ LastEvaluatedKey : [ ] ,
93+ Items : null
94+ } ) ) ;
95+
96+ } else {
97+ throw error ;
98+ }
99+ } ) ;
78100
79101 for ( const item of Items ) {
80102 executedItems . push ( item [ this . attributeName ] ) ;
@@ -85,4 +107,26 @@ export default class DynamoDBStorage implements Storage {
85107
86108 return sort ( ( a , b ) => a . localeCompare ( b ) , executedItems ) ;
87109 }
110+
111+ /**
112+ * Create migration table.
113+ *
114+ * @returns Promise
115+ */
116+ createMigrationTable ( ) {
117+ const params = {
118+ TableName : 'migrations' ,
119+ AttributeDefinitions : [
120+ { AttributeName : 'name' , AttributeType : 'S' } ,
121+ ] ,
122+ KeySchema : [
123+ { AttributeName : 'name' , KeyType : 'HASH' } ,
124+ ] ,
125+ ProvisionedThroughput : {
126+ ReadCapacityUnits : 1 ,
127+ WriteCapacityUnits : 1 ,
128+ }
129+ } ;
130+ return this . dynamodb . createTable ( params ) . promise ( ) ;
131+ }
88132}
0 commit comments