diff --git a/packages/@aws-cdk/aws-iot-actions/README.md b/packages/@aws-cdk/aws-iot-actions/README.md index 088fda5f3e5b8..2572003a0fab7 100644 --- a/packages/@aws-cdk/aws-iot-actions/README.md +++ b/packages/@aws-cdk/aws-iot-actions/README.md @@ -31,6 +31,7 @@ Currently supported are: - Put records to Kinesis Data Firehose stream - Send messages to SQS queues - Publish messages on SNS topics +- Write messages into columns of DynamoDB ## Republish a message to another MQTT topic @@ -278,3 +279,23 @@ const topicRule = new iot.TopicRule(this, 'TopicRule', { ], }); ``` + +## Write attributes of a message to DynamoDB + +The code snippet below creates an AWS IoT rule that writes all or part of an +MQTT message to DynamoDB using the DynamoDBv2 action. + +```ts +import * as dynamodb from '@aws-cdk/aws-dynamodb'; + +declare const table: dynamodb.Table; + +const topicRule = new iot.TopicRule(this, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323( + "SELECT * FROM 'device/+/data'", + ), + actions: [ + new actions.DynamoDBv2PutItemAction(table) + ], +}); +``` diff --git a/packages/@aws-cdk/aws-iot-actions/lib/dynamodbv2-put-item-action.ts b/packages/@aws-cdk/aws-iot-actions/lib/dynamodbv2-put-item-action.ts new file mode 100644 index 0000000000000..606ad3f0036f0 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/lib/dynamodbv2-put-item-action.ts @@ -0,0 +1,44 @@ +import * as dynamodb from '@aws-cdk/aws-dynamodb'; +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import { CommonActionProps } from './common-action-props'; +import { singletonActionRole } from './private/role'; + +/** + * Configuration properties of an action for the dynamodb table. + */ +export interface DynamoDBv2PutItemActionProps extends CommonActionProps { +} + +/** + * The action to put the record from an MQTT message to the DynamoDB table. + */ +export class DynamoDBv2PutItemAction implements iot.IAction { + private readonly role?: iam.IRole; + + /** + * @param table the DynamoDB table in which to put the items. + * @param props Optional properties to not use default + */ + constructor(private readonly table: dynamodb.ITable, props: DynamoDBv2PutItemActionProps = {}) { + this.role = props.role; + } + + bind(rule: iot.ITopicRule): iot.ActionConfig { + const role = this.role ?? singletonActionRole(rule); + role.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['dynamodb:PutItem'], + resources: [this.table.tableArn], + })); + return { + configuration: { + dynamoDBv2: { + putItem: { + tableName: this.table.tableName, + }, + roleArn: role.roleArn, + }, + }, + }; + } +} diff --git a/packages/@aws-cdk/aws-iot-actions/lib/index.ts b/packages/@aws-cdk/aws-iot-actions/lib/index.ts index 5c214f4143309..91dd24ac32cd1 100644 --- a/packages/@aws-cdk/aws-iot-actions/lib/index.ts +++ b/packages/@aws-cdk/aws-iot-actions/lib/index.ts @@ -2,6 +2,7 @@ export * from './cloudwatch-logs-action'; export * from './cloudwatch-put-metric-action'; export * from './cloudwatch-set-alarm-state-action'; export * from './common-action-props'; +export * from './dynamodbv2-put-item-action'; export * from './firehose-put-record-action'; export * from './iot-republish-action'; export * from './kinesis-put-record-action'; diff --git a/packages/@aws-cdk/aws-iot-actions/package-lock.json b/packages/@aws-cdk/aws-iot-actions/package-lock.json new file mode 100644 index 0000000000000..094a8191aafc5 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/package-lock.json @@ -0,0 +1,173 @@ +{ + "name": "@aws-cdk/aws-iot-actions", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@aws-cdk/cloud-assembly-schema": { + "version": "1.166.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-1.166.1.tgz", + "integrity": "sha512-QL8IKpaUngtqQipLO6/7uvbR7HE8CTr2z3JLKcLjofountigs6l4VdL8wR6XMbzZ18DaOJ6iLWHGpbT4BBoHPg==", + "requires": { + "jsonschema": "^1.4.1", + "semver": "^7.3.7" + }, + "dependencies": { + "jsonschema": { + "version": "1.4.1", + "bundled": true + }, + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.7", + "bundled": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true + } + } + }, + "@aws-cdk/cx-api": { + "version": "1.166.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/cx-api/-/cx-api-1.166.1.tgz", + "integrity": "sha512-cCsRe8wRGVhGaKFcW2l4b2LInzc9FeDsoaRTRreT3I+MxYyCAYlwWmx0RYoLg6r5eq6TD9PLAbOcCC6Y46CZ1g==", + "requires": { + "@aws-cdk/cloud-assembly-schema": "1.166.1", + "semver": "^7.3.7" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.7", + "bundled": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true + } + } + }, + "@aws-cdk/integ-tests": { + "version": "1.166.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/integ-tests/-/integ-tests-1.166.1.tgz", + "integrity": "sha512-x+KHKQDjKYbqTEUnTcbQa7Ubqu25N3/6PTeXpYs2staBB0HQPySjwbY87I6Lxpi1AexlJb+YhwfBofC7B/qoBQ==", + "requires": { + "@aws-cdk/cloud-assembly-schema": "1.166.1", + "@aws-cdk/core": "1.166.1", + "constructs": "^3.3.69" + }, + "dependencies": { + "@aws-cdk/core": { + "version": "1.166.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/core/-/core-1.166.1.tgz", + "integrity": "sha512-C0xNgePI1o6+RJqiXSG3RgiQu7q9uKf56QxeRgvCLsgDDrS/DBZFe7erAC/cO6R7mxhqyOOIWJSHuCdUWPmkqg==", + "requires": { + "@aws-cdk/cloud-assembly-schema": "1.166.1", + "@aws-cdk/cx-api": "1.166.1", + "@aws-cdk/region-info": "1.166.1", + "@balena/dockerignore": "^1.0.2", + "constructs": "^3.3.69", + "fs-extra": "^9.1.0", + "ignore": "^5.2.0", + "minimatch": "^3.1.2" + }, + "dependencies": { + "@balena/dockerignore": { + "version": "1.0.2", + "bundled": true + }, + "at-least-node": { + "version": "1.0.0", + "bundled": true + }, + "balanced-match": { + "version": "1.0.2", + "bundled": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "bundled": true + }, + "fs-extra": { + "version": "9.1.0", + "bundled": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "bundled": true + }, + "ignore": { + "version": "5.2.0", + "bundled": true + }, + "jsonfile": { + "version": "6.1.0", + "bundled": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "bundled": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "universalify": { + "version": "2.0.0", + "bundled": true + } + } + }, + "constructs": { + "version": "3.4.57", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-3.4.57.tgz", + "integrity": "sha512-jQyF6jFUJFqew13gDMZ9G8TkgFIJ7jMIG56FX9i3j/4pfGHrT+zwu6C95uuFIrBveC2RctxuDup23xBzs/XgUA==" + } + } + }, + "@aws-cdk/region-info": { + "version": "1.166.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/region-info/-/region-info-1.166.1.tgz", + "integrity": "sha512-yxjMjXNhG+Iqo04P1U0JNBGeR3kfDe3Ecc72ywuxi+QH/bhCEGnf+783BjYqXHWh4olTwZbMYQ/BcsOO0Dchkw==" + }, + "case": { + "version": "1.6.3" + } + } +} diff --git a/packages/@aws-cdk/aws-iot-actions/package.json b/packages/@aws-cdk/aws-iot-actions/package.json index 769fa94121569..44b052ec6db66 100644 --- a/packages/@aws-cdk/aws-iot-actions/package.json +++ b/packages/@aws-cdk/aws-iot-actions/package.json @@ -81,6 +81,7 @@ "@aws-cdk/aws-kinesisfirehose-destinations": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/integ-runner": "0.0.0", + "@aws-cdk/integ-tests": "0.0.0", "@aws-cdk/pkglint": "0.0.0", "@types/jest": "^27.5.2", "jest": "^27.5.1", @@ -88,6 +89,7 @@ }, "dependencies": { "@aws-cdk/aws-cloudwatch": "0.0.0", + "@aws-cdk/aws-dynamodb": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-iot": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", @@ -104,6 +106,7 @@ "homepage": "https://github.com/aws/aws-cdk", "peerDependencies": { "@aws-cdk/aws-cloudwatch": "0.0.0", + "@aws-cdk/aws-dynamodb": "0.0.0", "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-iot": "0.0.0", "@aws-cdk/aws-kinesis": "0.0.0", diff --git a/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/cdk.out new file mode 100644 index 0000000000000..588d7b269d34f --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"20.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/dynamodbv2integtestDefaultTestDeployAssertEF9A9A37.template.json b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/dynamodbv2integtestDefaultTestDeployAssertEF9A9A37.template.json new file mode 100644 index 0000000000000..9e26dfeeb6e64 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/dynamodbv2integtestDefaultTestDeployAssertEF9A9A37.template.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/integ.json b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/integ.json new file mode 100644 index 0000000000000..c30fa81cf3bd9 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/integ.json @@ -0,0 +1,11 @@ +{ + "version": "20.0.0", + "testCases": { + "dynamodbv2-integtest/DefaultTest": { + "stacks": [ + "test-dynamodbv2-put-item-action-stack" + ], + "assertionStack": "dynamodbv2integtestDefaultTestDeployAssertEF9A9A37" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/manifest.json new file mode 100644 index 0000000000000..50a2778c1a195 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/manifest.json @@ -0,0 +1,63 @@ +{ + "version": "20.0.0", + "artifacts": { + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + }, + "test-dynamodbv2-put-item-action-stack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "test-dynamodbv2-put-item-action-stack.template.json", + "validateOnSynth": false + }, + "metadata": { + "/test-dynamodbv2-put-item-action-stack/TopicRule/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TopicRule40A4EA44" + } + ], + "/test-dynamodbv2-put-item-action-stack/TopicRule/TopicRuleActionRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TopicRuleTopicRuleActionRole246C4F77" + } + ], + "/test-dynamodbv2-put-item-action-stack/TopicRule/TopicRuleActionRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687" + } + ], + "/test-dynamodbv2-put-item-action-stack/MyTable": [ + { + "type": "aws:cdk:hasPhysicalName", + "data": { + "Ref": "MyTable794EDED1" + } + } + ], + "/test-dynamodbv2-put-item-action-stack/MyTable/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "MyTable794EDED1" + } + ] + }, + "displayName": "test-dynamodbv2-put-item-action-stack" + }, + "dynamodbv2integtestDefaultTestDeployAssertEF9A9A37": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "dynamodbv2integtestDefaultTestDeployAssertEF9A9A37.template.json", + "validateOnSynth": false + }, + "displayName": "dynamodbv2-integtest/DefaultTest/DeployAssert" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/test-dynamodbv2-put-item-action-stack.template.json b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/test-dynamodbv2-put-item-action-stack.template.json new file mode 100644 index 0000000000000..d7fa86d112450 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/test-dynamodbv2-put-item-action-stack.template.json @@ -0,0 +1,105 @@ +{ + "Resources": { + "TopicRule40A4EA44": { + "Type": "AWS::IoT::TopicRule", + "Properties": { + "TopicRulePayload": { + "Actions": [ + { + "DynamoDBv2": { + "PutItem": { + "TableName": { + "Ref": "MyTable794EDED1" + } + }, + "RoleArn": { + "Fn::GetAtt": [ + "TopicRuleTopicRuleActionRole246C4F77", + "Arn" + ] + } + } + } + ], + "AwsIotSqlVersion": "2016-03-23", + "Sql": "SELECT * FROM 'device/+/data'" + } + } + }, + "TopicRuleTopicRuleActionRole246C4F77": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "iot.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "dynamodb:PutItem", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyTable794EDED1", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687", + "Roles": [ + { + "Ref": "TopicRuleTopicRuleActionRole246C4F77" + } + ] + } + }, + "MyTable794EDED1": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "KeySchema": [ + { + "AttributeName": "hashKey", + "KeyType": "HASH" + }, + { + "AttributeName": "sortKey", + "KeyType": "RANGE" + } + ], + "AttributeDefinitions": [ + { + "AttributeName": "hashKey", + "AttributeType": "S" + }, + { + "AttributeName": "sortKey", + "AttributeType": "N" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": 1, + "WriteCapacityUnits": 1 + }, + "TableName": "MyTable" + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/tree.json b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/tree.json new file mode 100644 index 0000000000000..995b3a6a9f21f --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.integ.snapshot/tree.json @@ -0,0 +1,246 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.63" + } + }, + "test-dynamodbv2-put-item-action-stack": { + "id": "test-dynamodbv2-put-item-action-stack", + "path": "test-dynamodbv2-put-item-action-stack", + "children": { + "TopicRule": { + "id": "TopicRule", + "path": "test-dynamodbv2-put-item-action-stack/TopicRule", + "children": { + "Resource": { + "id": "Resource", + "path": "test-dynamodbv2-put-item-action-stack/TopicRule/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IoT::TopicRule", + "aws:cdk:cloudformation:props": { + "topicRulePayload": { + "actions": [ + { + "dynamoDBv2": { + "putItem": { + "tableName": { + "Ref": "MyTable794EDED1" + } + }, + "roleArn": { + "Fn::GetAtt": [ + "TopicRuleTopicRuleActionRole246C4F77", + "Arn" + ] + } + } + } + ], + "awsIotSqlVersion": "2016-03-23", + "sql": "SELECT * FROM 'device/+/data'" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iot.CfnTopicRule", + "version": "0.0.0" + } + }, + "TopicRuleActionRole": { + "id": "TopicRuleActionRole", + "path": "test-dynamodbv2-put-item-action-stack/TopicRule/TopicRuleActionRole", + "children": { + "Resource": { + "id": "Resource", + "path": "test-dynamodbv2-put-item-action-stack/TopicRule/TopicRuleActionRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "assumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "iot.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "test-dynamodbv2-put-item-action-stack/TopicRule/TopicRuleActionRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "test-dynamodbv2-put-item-action-stack/TopicRule/TopicRuleActionRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "dynamodb:PutItem", + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "MyTable794EDED1", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "TopicRuleTopicRuleActionRoleDefaultPolicy99ADD687", + "roles": [ + { + "Ref": "TopicRuleTopicRuleActionRole246C4F77" + } + ] + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iam.Role", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-iot.TopicRule", + "version": "0.0.0" + } + }, + "MyTable": { + "id": "MyTable", + "path": "test-dynamodbv2-put-item-action-stack/MyTable", + "children": { + "Resource": { + "id": "Resource", + "path": "test-dynamodbv2-put-item-action-stack/MyTable/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DynamoDB::Table", + "aws:cdk:cloudformation:props": { + "keySchema": [ + { + "attributeName": "hashKey", + "keyType": "HASH" + }, + { + "attributeName": "sortKey", + "keyType": "RANGE" + } + ], + "attributeDefinitions": [ + { + "attributeName": "hashKey", + "attributeType": "S" + }, + { + "attributeName": "sortKey", + "attributeType": "N" + } + ], + "provisionedThroughput": { + "readCapacityUnits": 1, + "writeCapacityUnits": 1 + }, + "tableName": "MyTable" + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-dynamodb.CfnTable", + "version": "0.0.0" + } + }, + "ScalingRole": { + "id": "ScalingRole", + "path": "test-dynamodbv2-put-item-action-stack/MyTable/ScalingRole", + "constructInfo": { + "fqn": "@aws-cdk/core.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/aws-dynamodb.Table", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + }, + "dynamodbv2-integtest": { + "id": "dynamodbv2-integtest", + "path": "dynamodbv2-integtest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "dynamodbv2-integtest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "dynamodbv2-integtest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.1.63" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "dynamodbv2-integtest/DefaultTest/DeployAssert", + "constructInfo": { + "fqn": "@aws-cdk/core.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests.IntegTest", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/core.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.test.ts b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.test.ts new file mode 100644 index 0000000000000..f4ddf926ed6f5 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/dynamodbv2-put-item-action.test.ts @@ -0,0 +1,96 @@ +import { Template, Match } from '@aws-cdk/assertions'; +import * as dynamodb from '@aws-cdk/aws-dynamodb'; +import * as iam from '@aws-cdk/aws-iam'; +import * as iot from '@aws-cdk/aws-iot'; +import * as cdk from '@aws-cdk/core'; +import * as actions from '../../lib'; + +test('Default dynamoDBv2 action', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const table = dynamodb.Table.fromTableArn(stack, 'MyTable', 'arn:aws:dynamodb:xx-west-1:111122223333:table/my-table'); + + // WHEN + topicRule.addAction(new actions.DynamoDBv2PutItemAction(table)); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + { + DynamoDBv2: { + PutItem: { + TableName: 'my-table', + }, + RoleArn: { + 'Fn::GetAtt': ['MyTopicRuleTopicRuleActionRoleCE2D05DA', 'Arn'], + }, + }, + }, + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'iot.amazonaws.com', + }, + }, + ], + Version: '2012-10-17', + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'dynamodb:PutItem', + Effect: 'Allow', + Resource: 'arn:aws:dynamodb:xx-west-1:111122223333:table/my-table', + }, + ], + Version: '2012-10-17', + }, + PolicyName: 'MyTopicRuleTopicRuleActionRoleDefaultPolicy54A701F7', + Roles: [ + { Ref: 'MyTopicRuleTopicRuleActionRoleCE2D05DA' }, + ], + }); +}); + + +test('can set role', () => { + // GIVEN + const stack = new cdk.Stack(); + const topicRule = new iot.TopicRule(stack, 'MyTopicRule', { + sql: iot.IotSql.fromStringAsVer20160323("SELECT topic(2) as device_id FROM 'device/+/data'"), + }); + const table = dynamodb.Table.fromTableArn(stack, 'MyTable', 'arn:aws:dynamodb:xx-west-1:111122223333:table/my-table'); + const role = iam.Role.fromRoleArn(stack, 'MyRole', 'arn:aws:iam::123456789012:role/ForTest'); + + // WHEN + topicRule.addAction(new actions.DynamoDBv2PutItemAction(table, { role })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', { + TopicRulePayload: { + Actions: [ + Match.objectLike({ DynamoDBv2: { RoleArn: 'arn:aws:iam::123456789012:role/ForTest' } }), + ], + }, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + PolicyName: 'MyRolePolicy64AB00A5', + Roles: ['ForTest'], + }); +}); diff --git a/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/integ.dynamodbv2-put-item-action.ts b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/integ.dynamodbv2-put-item-action.ts new file mode 100644 index 0000000000000..1dc0e7c2eb509 --- /dev/null +++ b/packages/@aws-cdk/aws-iot-actions/test/dynamodbv2/integ.dynamodbv2-put-item-action.ts @@ -0,0 +1,41 @@ +import * as dynamodb from '@aws-cdk/aws-dynamodb'; +import * as iot from '@aws-cdk/aws-iot'; +import * as cdk from '@aws-cdk/core'; +import * as integ from '@aws-cdk/integ-tests'; +import * as actions from '../../lib'; + +const app = new cdk.App(); +const stack = new cdk.Stack(app, 'test-dynamodbv2-put-item-action-stack'); + +const topicRule = new iot.TopicRule(stack, 'TopicRule', { + sql: iot.IotSql.fromStringAsVer20160323( + "SELECT * FROM 'device/+/data'", + ), +}); + +const tablePartitionKey: dynamodb.Attribute = { + name: 'hashKey', + type: dynamodb.AttributeType.STRING, +}; + +const tableSortKey: dynamodb.Attribute = { + name: 'sortKey', + type: dynamodb.AttributeType.NUMBER, +}; + +const table = new dynamodb.Table(stack, 'MyTable', { + tableName: 'MyTable', + readCapacity: 1, + writeCapacity: 1, + partitionKey: tablePartitionKey, + sortKey: tableSortKey, + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); + +topicRule.addAction(new actions.DynamoDBv2PutItemAction(table)); + +new integ.IntegTest(app, 'dynamodbv2-integtest', { + testCases: [stack], +}); + +app.synth();