Skip to content

Commit

Permalink
feat(iot): implements some actions
Browse files Browse the repository at this point in the history
  • Loading branch information
yamatatsu committed Oct 4, 2021
1 parent bdd64f4 commit 924ab83
Show file tree
Hide file tree
Showing 45 changed files with 3,713 additions and 4 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@
"@aws-cdk/aws-eks/yaml/**",
"@aws-cdk/aws-events-targets/aws-sdk",
"@aws-cdk/aws-events-targets/aws-sdk/**",
"@aws-cdk/aws-iot/case",
"@aws-cdk/aws-iot/case/**",
"@aws-cdk/aws-s3-deployment/case",
"@aws-cdk/aws-s3-deployment/case/**",
"@aws-cdk/cloud-assembly-schema/jsonschema",
Expand Down
30 changes: 30 additions & 0 deletions packages/@aws-cdk/aws-iot/NOTICE
Original file line number Diff line number Diff line change
@@ -1,2 +1,32 @@
AWS Cloud Development Kit (AWS CDK)
Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.

-------------------------------------------------------------------------------

The AWS CDK includes the following third-party software/licensing:

** case - https://www.npmjs.com/package/case
Copyright (c) 2013 Nathan Bubna

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

----------------
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-iot/lib/action.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IConstruct } from '@aws-cdk/core';
import { CfnTopicRule } from './iot.generated';
import { ITopicRule } from './topic-rule-ref';

/**
* An abstract action for TopicRule.
Expand All @@ -10,7 +10,7 @@ export interface IAction {
*
* @param rule The TopicRule that would trigger this action.
*/
bind(rule: IConstruct): ActionConfig;
bind(rule: ITopicRule): ActionConfig;
}

/**
Expand Down
73 changes: 73 additions & 0 deletions packages/@aws-cdk/aws-iot/lib/actions/cloudwatch-alarm-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import * as iam from '@aws-cdk/aws-iam';
import { IConstruct } from '@aws-cdk/core';
import { IAction, ActionConfig } from '..';
import { singletonActionRole } from './util';

/**
* Configuration properties of an action for CloudWatch alarm.
*/
export interface CloudwatchAlarmActionProps {
/**
* Reason for the alarm change.
*
* Supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*
* @default 'This state was set by the rule of AWS IoT Core.' will be set
*/
readonly stateReason?: string;
/**
* The IAM role that allows access to the CloudWatch alarm.
*
* @default a new role will be created
*/
readonly role?: iam.IRole;
}

/**
* The action to change the state of an Amazon CloudWatch alarm.
*/
export class CloudwatchAlarmAction implements IAction {
private readonly stateReason?: string;
private readonly role?: iam.IRole;

/**
* `stateValue` supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*
* @param alarm The CloudWatch alarm that set state by the rule
* @param stateValue The value of the alarm state.
* Valid values: OK, ALARM, INSUFFICIENT_DATA or substitution templates.
* @param props Optional properties to not use default
*/
constructor(
private readonly alarm: cloudwatch.IAlarm,
private readonly stateValue: cloudwatch.AlarmState | string,
props: CloudwatchAlarmActionProps = {},
) {
this.stateReason = props.stateReason;
this.role = props.role;
}

bind(rule: IConstruct): ActionConfig {
const role = this.role ?? singletonActionRole(rule);
role.addToPrincipalPolicy(this.putEventStatement(this.alarm));

return {
cloudwatchAlarm: {
alarmName: this.alarm.alarmName,
stateReason: this.stateReason ?? 'This state was set by the rule of AWS IoT Core.',
stateValue: this.stateValue,
roleArn: role.roleArn,
},
};
}

private putEventStatement(alarm: cloudwatch.IAlarm) {
return new iam.PolicyStatement({
actions: ['cloudwatch:SetAlarmState'],
resources: [alarm.alarmArn],
});
}
}
47 changes: 47 additions & 0 deletions packages/@aws-cdk/aws-iot/lib/actions/cloudwatch-logs-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as iam from '@aws-cdk/aws-iam';
import * as logs from '@aws-cdk/aws-logs';
import { IConstruct } from '@aws-cdk/core';
import { IAction, ActionConfig } from '..';
import { singletonActionRole } from './util';

/**
* Configuration properties of an action for CloudWatch Logs.
*/
export interface CloudwatchLogsActionProps {
/**
* The IAM role that allows access to the CloudWatch log group.
*
* @default a new role will be created
*/
readonly role?: iam.IRole;
}

/**
* The action to send data to Amazon CloudWatch Logs
*/
export class CloudwatchLogsAction implements IAction {
private readonly role?: iam.IRole;

/**
* @param logGroup The CloudWatch log group to which the action sends data
* @param props Optional properties to not use default
*/
constructor(
private readonly logGroup: logs.ILogGroup,
props: CloudwatchLogsActionProps = {},
) {
this.role = props.role;
}

bind(rule: IConstruct): ActionConfig {
const role = this.role ?? singletonActionRole(rule);
this.logGroup.grant(role, 'logs:CreateLogStream', 'logs:DescribeLogStreams', 'logs:PutLogEvents');

return {
cloudwatchLogs: {
logGroupName: this.logGroup.logGroupName,
roleArn: role.roleArn,
},
};
}
}
84 changes: 84 additions & 0 deletions packages/@aws-cdk/aws-iot/lib/actions/cloudwatch-metric-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import * as iam from '@aws-cdk/aws-iam';
import { IConstruct } from '@aws-cdk/core';
import { IAction, ActionConfig } from '..';
import { singletonActionRole } from './util';

/**
* Configuration properties of an action for CloudWatch metric.
*/
export interface CloudwatchMetricActionProps {
/**
* The CloudWatch metric name.
*
* Supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*/
readonly metricName: string,
/**
* The CloudWatch metric namespace name.
*
* Supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*/
readonly metricNamespace: string,
/**
* A string that contains the timestamp, expressed in seconds in Unix epoch time.
*
* Supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*
* @default None - Defaults to the current Unix epoch time.
*/
readonly metricTimestamp?: string,
/**
* The metric unit supported by CloudWatch.
*
* Supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*/
readonly metricUnit: string,
/**
* A string that contains the CloudWatch metric value.
*
* Supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*/
readonly metricValue: string,
/**
* The IAM role that allows access to the CloudWatch metric.
*
* @default a new role will be created
*/
readonly role?: iam.IRole;
}

/**
* The action to capture an Amazon CloudWatch metric.
*/
export class CloudwatchMetricAction implements IAction {
constructor(private readonly props: CloudwatchMetricActionProps) {
}

bind(rule: IConstruct): ActionConfig {
const role = this.props.role ?? singletonActionRole(rule);
role.addToPrincipalPolicy(this.putEventStatement());

return {
cloudwatchMetric: {
metricName: this.props.metricName,
metricNamespace: this.props.metricNamespace,
metricTimestamp: this.props.metricTimestamp,
metricUnit: this.props.metricUnit,
metricValue: this.props.metricValue,
roleArn: role.roleArn,
},
};
}

private putEventStatement() {
return new iam.PolicyStatement({
actions: ['cloudwatch:PutMetricData'],
resources: ['*'],
});
}
}
92 changes: 92 additions & 0 deletions packages/@aws-cdk/aws-iot/lib/actions/dynamodb-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as dynamodb from '@aws-cdk/aws-dynamodb';
import * as iam from '@aws-cdk/aws-iam';
import { IConstruct } from '@aws-cdk/core';
import { IAction, ActionConfig } from '..';
import { singletonActionRole } from './util';

/**
* Configuration properties of an action for DynamoDB.
*/
export interface DynamoDBActionProps {
/**
* The DynamoDB table.
*/
readonly table: dynamodb.Table,
/**
* The value of the partition key.
*
* Supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*/
readonly partitionKeyValue: string,
/**
* The value of the sort key.
*
* Supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*/
readonly sortKeyValue?: string,
/**
* The name of the column where the payload is written.
*
* Supports substitution templates.
* @see https://docs.aws.amazon.com/iot/latest/developerguide/iot-substitution-templates.html
*/
readonly payloadField?: string,
/**
* The IAM role that allows access to the DynamoDB.
*
* @default a new role will be created
*/
readonly role?: iam.IRole,
}

export enum KeyType {
STRING = 'STRING',
NUMBER = 'NUMBER',
}

/**
* The action to write all or part of an MQTT message to an Amazon DynamoDB table.
*/
export class DynamoDBAction implements IAction {
constructor(private readonly props: DynamoDBActionProps) {
}

bind(rule: IConstruct): ActionConfig {
const role = this.props.role ?? singletonActionRole(rule);
this.props.table.grant(role, 'dynamodb:PutItem');

const { partitionKey, sortKey } = this.props.table.schema();

return {
dynamoDb: {
tableName: this.props.table.tableName,
hashKeyField: partitionKey.name,
hashKeyType: convertToKeyType(partitionKey.type),
hashKeyValue: this.props.partitionKeyValue,
rangeKeyField: sortKey?.name,
rangeKeyType: convertToKeyType(sortKey?.type),
rangeKeyValue: this.props.sortKeyValue,
payloadField: this.props.payloadField,
roleArn: role.roleArn,
},
};
}
}

function convertToKeyType(attributeType?: dynamodb.AttributeType): KeyType | undefined {
if (!attributeType) {
return;
}

switch (attributeType) {
case dynamodb.AttributeType.STRING:
return KeyType.STRING;
case dynamodb.AttributeType.NUMBER:
return KeyType.NUMBER;
case dynamodb.AttributeType.BINARY:
throw new Error('DynamoDB Action doesn\'t support binary attribute type.');
}

}
Loading

0 comments on commit 924ab83

Please sign in to comment.