From 6c13b0583349d5b6af146926cf93b3df1540c781 Mon Sep 17 00:00:00 2001 From: Jungseok Lee Date: Sun, 16 Sep 2018 15:22:45 -0700 Subject: [PATCH] BREAKING: Introduce an interface for DynamoDB attribute This patch introduces an interface to represent DynamoDB attribute and changes signature of add{Partition|SortKey} methods. --- docs/src/examples.rst | 4 +- .../chat-app/dynamodb-posts-table.ts | 4 +- .../hello-cdk/index.ts | 4 +- packages/@aws-cdk/aws-dynamodb/lib/table.ts | 26 +++- .../aws-dynamodb/test/integ.dynamodb.ts | 6 +- .../aws-dynamodb/test/test.dynamodb.ts | 137 +++++++++--------- 6 files changed, 100 insertions(+), 81 deletions(-) diff --git a/docs/src/examples.rst b/docs/src/examples.rst index 018cd72f12047..89aeb49b14ed9 100644 --- a/docs/src/examples.rst +++ b/docs/src/examples.rst @@ -135,8 +135,8 @@ and sort key **Timestamp**. writeCapacity: 5 }); - table.addPartitionKey('Alias', dynamodb.KeyAttributeType.String); - table.addSortKey('Timestamp', dynamodb.KeyAttributeType.String); + table.addPartitionKey({ name: 'Alias', type: dynamodb.AttributeType.String }); + table.addSortKey({ name: 'Timestamp', type: dynamodb.AttributeType.String }); } } diff --git a/examples/cdk-examples-typescript/chat-app/dynamodb-posts-table.ts b/examples/cdk-examples-typescript/chat-app/dynamodb-posts-table.ts index ab46fe1bab0de..0a92f6ab9927a 100644 --- a/examples/cdk-examples-typescript/chat-app/dynamodb-posts-table.ts +++ b/examples/cdk-examples-typescript/chat-app/dynamodb-posts-table.ts @@ -9,7 +9,7 @@ export class DynamoPostsTable extends cdk.Construct { readCapacity: 5, writeCapacity: 5 }); - table.addPartitionKey('Alias', dynamodb.KeyAttributeType.String); - table.addSortKey('Timestamp', dynamodb.KeyAttributeType.String); + table.addPartitionKey({ name: 'Alias', type: dynamodb.AttributeType.String }); + table.addSortKey({ name: 'Timestamp', type: dynamodb.AttributeType.String }); } } diff --git a/examples/cdk-examples-typescript/hello-cdk/index.ts b/examples/cdk-examples-typescript/hello-cdk/index.ts index 204145630a2d1..f6f7f0ae08224 100644 --- a/examples/cdk-examples-typescript/hello-cdk/index.ts +++ b/examples/cdk-examples-typescript/hello-cdk/index.ts @@ -10,8 +10,8 @@ class HelloCDK extends cdk.Stack { writeCapacity: 1 }); - table.addPartitionKey('ID', dynamodb.KeyAttributeType.String); - table.addSortKey('Timestamp', dynamodb.KeyAttributeType.Number); + table.addPartitionKey({ name: 'ID', type: dynamodb.AttributeType.String }); + table.addSortKey({ name: 'Timestamp', type: dynamodb.AttributeType.Number }); } } diff --git a/packages/@aws-cdk/aws-dynamodb/lib/table.ts b/packages/@aws-cdk/aws-dynamodb/lib/table.ts index 19d80913e98bc..bdea8cce43450 100644 --- a/packages/@aws-cdk/aws-dynamodb/lib/table.ts +++ b/packages/@aws-cdk/aws-dynamodb/lib/table.ts @@ -66,6 +66,18 @@ export interface TableProps { writeAutoScaling?: AutoScalingProps; } +export interface Attribute { + /** + * The name of an attribute. + */ + name: string; + + /** + * The data type of an attribute. + */ + type: AttributeType; +} + /* tslint:disable:max-line-length */ export interface AutoScalingProps { /** @@ -150,13 +162,13 @@ export class Table extends Construct { } } - public addPartitionKey(name: string, type: KeyAttributeType): this { - this.addKey(name, type, HASH_KEY_TYPE); + public addPartitionKey(attribute: Attribute): this { + this.addKey(attribute.name, attribute.type, HASH_KEY_TYPE); return this; } - public addSortKey(name: string, type: KeyAttributeType): this { - this.addKey(name, type, RANGE_KEY_TYPE); + public addSortKey(attribute: Attribute): this { + this.addKey(attribute.name, attribute.type, RANGE_KEY_TYPE); return this; } @@ -266,7 +278,7 @@ export class Table extends Construct { return this.keySchema.find(prop => prop.keyType === keyType); } - private addKey(name: string, type: KeyAttributeType, keyType: string) { + private addKey(name: string, type: AttributeType, keyType: string) { const existingProp = this.findKey(keyType); if (existingProp) { throw new Error(`Unable to set ${name} as a ${keyType} key, because ${existingProp.attributeName} is a ${keyType} key`); @@ -279,7 +291,7 @@ export class Table extends Construct { return this; } - private registerAttribute(name: string, type: KeyAttributeType) { + private registerAttribute(name: string, type: AttributeType) { const existingDef = this.attributeDefinitions.find(def => def.attributeName === name); if (existingDef && existingDef.attributeType !== type) { throw new Error(`Unable to specify ${name} as ${type} because it was already defined as ${existingDef.attributeType}`); @@ -293,7 +305,7 @@ export class Table extends Construct { } } -export enum KeyAttributeType { +export enum AttributeType { Binary = 'B', Number = 'N', String = 'S', diff --git a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts index aeafbe014c35c..51e5cf04cf21e 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/integ.dynamodb.ts @@ -1,5 +1,5 @@ import { App, Stack } from '@aws-cdk/cdk'; -import { KeyAttributeType, StreamViewType, Table } from '../lib'; +import { AttributeType, StreamViewType, Table } from '../lib'; const app = new App(process.argv); @@ -12,7 +12,7 @@ const table = new Table(stack, 'Table', { ttlAttributeName: 'timeToLive' }); -table.addPartitionKey('hashKey', KeyAttributeType.String); -table.addSortKey('rangeKey', KeyAttributeType.Number); +table.addPartitionKey({ name: 'hashKey', type: AttributeType.String }); +table.addSortKey({ name: 'rangeKey', type: AttributeType.Number }); process.stdout.write(app.run()); diff --git a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts index 710d04e9b8cb9..52994b13d2e53 100644 --- a/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts +++ b/packages/@aws-cdk/aws-dynamodb/test/test.dynamodb.ts @@ -1,6 +1,6 @@ import { App, Stack } from '@aws-cdk/cdk'; import { Test } from 'nodeunit'; -import { KeyAttributeType, StreamViewType, Table } from '../lib'; +import { AttributeType, StreamViewType, Table } from '../lib'; export = { 'default properties': { @@ -14,7 +14,7 @@ export = { 'hash key only'(test: Test) { const app = new TestApp(); - new Table(app.stack, 'MyTable').addPartitionKey('hashKey', KeyAttributeType.Binary); + new Table(app.stack, 'MyTable').addPartitionKey({ name: 'hashKey', type: AttributeType.Binary }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -35,8 +35,9 @@ export = { 'hash + range key'(test: Test) { const app = new TestApp(); - new Table(app.stack, 'MyTable').addPartitionKey('hashKey', KeyAttributeType.Binary) - .addSortKey('sortKey', KeyAttributeType.Number); + new Table(app.stack, 'MyTable') + .addPartitionKey({ name: 'hashKey', type: AttributeType.Binary }) + .addSortKey({ name: 'sortKey', type: AttributeType.Number }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -60,11 +61,12 @@ export = { test.done(); }, + 'point-in-time recovery is not enabled'(test: Test) { const app = new TestApp(); new Table(app.stack, 'MyTable') - .addPartitionKey('partitionKey', KeyAttributeType.Binary) - .addSortKey('sortKey', KeyAttributeType.Number); + .addPartitionKey({ name: 'partitionKey', type: AttributeType.Binary }) + .addSortKey({ name: 'sortKey', type: AttributeType.Number }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -88,11 +90,12 @@ export = { test.done(); }, + 'server-side encryption is not enabled'(test: Test) { const app = new TestApp(); new Table(app.stack, 'MyTable') - .addPartitionKey('partitionKey', KeyAttributeType.Binary) - .addSortKey('sortKey', KeyAttributeType.Number); + .addPartitionKey({ name: 'partitionKey', type: AttributeType.Binary }) + .addSortKey({ name: 'sortKey', type: AttributeType.Number }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -116,11 +119,12 @@ export = { test.done(); }, + 'stream is not enabled'(test: Test) { const app = new TestApp(); new Table(app.stack, 'MyTable') - .addPartitionKey('partitionKey', KeyAttributeType.Binary) - .addSortKey('sortKey', KeyAttributeType.Number); + .addPartitionKey({ name: 'partitionKey', type: AttributeType.Binary }) + .addSortKey({ name: 'sortKey', type: AttributeType.Number }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -144,11 +148,12 @@ export = { test.done(); }, + 'ttl is not enabled'(test: Test) { const app = new TestApp(); new Table(app.stack, 'MyTable') - .addPartitionKey('partitionKey', KeyAttributeType.Binary) - .addSortKey('sortKey', KeyAttributeType.Number); + .addPartitionKey({ name: 'partitionKey', type: AttributeType.Binary }) + .addSortKey({ name: 'sortKey', type: AttributeType.Number }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -172,6 +177,7 @@ export = { test.done(); }, + 'can specify new and old images'(test: Test) { const app = new TestApp(); const table = new Table(app.stack, 'MyTable', { @@ -180,8 +186,8 @@ export = { writeCapacity: 1337, streamSpecification: StreamViewType.NewAndOldImages }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -207,6 +213,7 @@ export = { test.done(); }, + 'can specify new images only'(test: Test) { const app = new TestApp(); const table = new Table(app.stack, 'MyTable', { @@ -215,8 +222,8 @@ export = { writeCapacity: 1337, streamSpecification: StreamViewType.NewImage }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -242,6 +249,7 @@ export = { test.done(); }, + 'can specify old images only'(test: Test) { const app = new TestApp(); const table = new Table(app.stack, 'MyTable', { @@ -250,8 +258,8 @@ export = { writeCapacity: 1337, streamSpecification: StreamViewType.OldImage }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -290,8 +298,8 @@ export = { streamSpecification: StreamViewType.KeysOnly, ttlAttributeName: 'timeToLive' }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); const template = app.synthesizeTemplate(); test.deepEqual(template, { @@ -331,8 +339,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); table.addReadAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -419,8 +427,8 @@ export = { scalingPolicyName: 'MyAwesomePolicyName' } }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); const template = app.synthesizeTemplate(); test.deepEqual(template, { Resources: @@ -499,8 +507,8 @@ export = { scalingPolicyName: 'MyAwesomePolicyName' } }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addReadAutoScaling({ minCapacity: 500, maxCapacity: 5000, @@ -520,8 +528,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); table.addReadAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -599,8 +607,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); table.addReadAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -678,8 +686,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addReadAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -699,8 +707,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addReadAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -720,8 +728,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addReadAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -740,8 +748,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addReadAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -760,8 +768,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addReadAutoScaling({ minCapacity: 50, maxCapacity: -5, @@ -780,8 +788,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addReadAutoScaling({ minCapacity: -5, maxCapacity: 500, @@ -800,8 +808,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); table.addWriteAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -888,8 +896,8 @@ export = { scalingPolicyName: 'MyAwesomePolicyName' } }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); const template = app.synthesizeTemplate(); test.deepEqual(template, { Resources: @@ -968,8 +976,8 @@ export = { scalingPolicyName: 'MyAwesomePolicyName' } }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addWriteAutoScaling({ minCapacity: 500, maxCapacity: 5000, @@ -989,8 +997,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); table.addWriteAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -1068,8 +1076,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); table.addWriteAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -1147,8 +1155,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addWriteAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -1168,8 +1176,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addWriteAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -1189,8 +1197,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addWriteAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -1209,8 +1217,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addWriteAutoScaling({ minCapacity: 50, maxCapacity: 500, @@ -1229,8 +1237,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addWriteAutoScaling({ minCapacity: 50, maxCapacity: -5, @@ -1249,8 +1257,8 @@ export = { readCapacity: 42, writeCapacity: 1337 }); - table.addPartitionKey('partitionKey', KeyAttributeType.String); - table.addSortKey('sortKey', KeyAttributeType.Binary); + table.addPartitionKey({ name: 'partitionKey', type: AttributeType.String }); + table.addSortKey({ name: 'sortKey', type: AttributeType.Binary }); test.throws(() => table.addWriteAutoScaling({ minCapacity: -5, maxCapacity: 500, @@ -1261,7 +1269,6 @@ export = { test.done(); } - }; class TestApp {