Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(iot-actions): iot rule https action l2 construct #25535

Merged
merged 22 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
95d0806
added iot https action
May 11, 2023
7e72972
Merge branch 'main' into 25491-iot-https-action
cponfick May 11, 2023
6843d20
added section to readme
May 11, 2023
cfc65f1
fixed linting in readme
May 11, 2023
2df4b7b
adjusted title
May 11, 2023
946315d
removed imports from readme
May 11, 2023
3008530
Merge branch 'main' into 25491-iot-https-action
cponfick May 11, 2023
a8e6198
Merge branch 'main' into 25491-iot-https-action
cponfick May 12, 2023
7a5b478
Merge branch 'main' into 25491-iot-https-action
cponfick May 15, 2023
784a885
Merge branch 'main' into 25491-iot-https-action
cponfick May 16, 2023
fd7097d
Merge branch 'main' into 25491-iot-https-action
cponfick May 17, 2023
7797130
Merge branch 'main' into 25491-iot-https-action
cponfick May 19, 2023
e4923a2
Merge branch 'main' into 25491-iot-https-action
cponfick May 22, 2023
2f07fec
Merge branch 'main' into 25491-iot-https-action
cponfick May 23, 2023
698ad9b
Merge branch 'main' into 25491-iot-https-action
cponfick May 24, 2023
7e8acce
Merge branch 'main' into 25491-iot-https-action
cponfick May 24, 2023
13dd259
Merge branch 'main' into 25491-iot-https-action
cponfick May 31, 2023
67216b4
Merge branch 'main' into 25491-iot-https-action
cponfick Jun 1, 2023
5805329
Merge branch 'main' into 25491-iot-https-action
cponfick Jun 15, 2023
0c268f8
Merge branch 'main' into 25491-iot-https-action
cponfick Jun 19, 2023
87fc60e
Merge branch 'main' into 25491-iot-https-action
cponfick Jul 2, 2023
21b1ad2
Merge branch 'main' into 25491-iot-https-action
mergify[bot] Jul 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions packages/@aws-cdk/aws-iot-actions-alpha/lib/https-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import * as iot from '@aws-cdk/aws-iot-alpha';
import * as iam from 'aws-cdk-lib/aws-iam';
import { CommonActionProps } from './common-action-props';
import { singletonActionRole } from './private/role';

export interface HttpActionSigV4Auth {
/**
* The service name.
*/
readonly serviceName: string;
/**
* The signing region.
*/
readonly signingRegion: string;
}

export interface HttpActionHeader {
/**
* The HTTP header key.
*/
readonly key: string;
/**
* The HTTP header value. Substitution templates are supported.
*/
readonly value: string;
}

/**
* Configuration properties of an HTTPS action.
*
* @see https://docs.aws.amazon.com/iot/latest/developerguide/https-rule-action.html
*/
export interface HttpsActionProps extends CommonActionProps {
/**
* If specified, AWS IoT uses the confirmation URL to create a matching topic rule destination.
*/
readonly confirmationUrl?: string;

/**
* The headers to include in the HTTPS request to the endpoint.
*/
readonly headers?: Array<HttpActionHeader>;

/**
* Use Sigv4 authorization.
*/
readonly auth?: HttpActionSigV4Auth;
}

/**
* The action to send data from an MQTT message to a web application or service.
*/
export class HttpsAction implements iot.IAction {
private readonly role?: iam.IRole;
private readonly url: string;
private readonly confirmationUrl?: string;
private readonly headers?: Array<HttpActionHeader>;
private readonly auth?: HttpActionSigV4Auth;

/**
* @param url The url to which to send post request.
* @param props Optional properties to not use default.
*/
constructor( url: string, props: HttpsActionProps={}) {
this.url = url;
this.confirmationUrl = props.confirmationUrl;
this.headers = props.headers;
this.role = props.role;
this.auth = props.auth;
}

/**
* @internal
*/
public _bind(topicRule: iot.ITopicRule): iot.ActionConfig {
const role = this.role ?? singletonActionRole(topicRule);
const sigV4 = this.auth ? {
sigv4: {
roleArn: role.roleArn,
serviceName: this.auth.serviceName,
signingRegion: this.auth.signingRegion,
},
} : this.auth;

return {
configuration: {
http: {
url: this.url,
confirmationUrl: this.confirmationUrl,
headers: this.headers,
auth: sigV4,
},
},
};
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-iot-actions-alpha/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './lambda-function-action';
export * from './s3-put-object-action';
export * from './sqs-queue-action';
export * from './sns-topic-action';
export * from './https-action';
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { Template, Match } from 'aws-cdk-lib/assertions';
import * as iot from '@aws-cdk/aws-iot-alpha';
import * as cdk from 'aws-cdk-lib';
import * as actions from '../../lib';

test('Default HTTPS 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 expectedUrl = 'https://example.com';

// WHEN
topicRule.addAction(new actions.HttpsAction(expectedUrl));

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', {
TopicRulePayload: {
Actions: [
{
Http: {
Url: expectedUrl,
},
},
],
},
});

Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', {
AssumeRolePolicyDocument: {
Statement: [
{
Action: 'sts:AssumeRole',
Effect: 'Allow',
Principal: {
Service: 'iot.amazonaws.com',
},
},
],
Version: '2012-10-17',
},
});
});

test('can set confirmation url', () => {
// 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 expectedUrl = 'https://example.com';
const expectedConfirmationUrl = 'https://example.com/confirm';

// WHEN
topicRule.addAction(
new actions.HttpsAction(expectedUrl, {
confirmationUrl: expectedConfirmationUrl,
}),
);

//THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', {
TopicRulePayload: {
Actions: [
{
Http: {
Url: expectedUrl,
ConfirmationUrl: expectedConfirmationUrl,
},
},
],
},
});
});

test('can set http headers', () => {
// 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 expectedUrl = 'https://example.com';
const headers = [
{ key: 'key0', value: 'value0' },
{ key: 'key1', value: 'value1' },
];

// WHEN
topicRule.addAction(
new actions.HttpsAction(expectedUrl, { headers: headers }),
);

//THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', {
TopicRulePayload: {
Actions: [
{
Http: {
Url: expectedUrl,
Headers: [
{ Key: 'key0', Value: 'value0' },
{ Key: 'key1', Value: 'value1' },
],
},
},
],
},
});
});

test('can set http auth', () => {
// 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 expectedUrl = 'https://example.com';
const expectedAuth = {
serviceName: 'serviceName',
signingRegion: 'signingName',
};

// WHEN
topicRule.addAction(
new actions.HttpsAction(expectedUrl, { auth: expectedAuth }),
);

//THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoT::TopicRule', {
TopicRulePayload: {
Actions: [
{
Http: {
Url: expectedUrl,
Auth: {
Sigv4: {
RoleArn: {
'Fn::GetAtt': [
Match.stringLikeRegexp('MyTopicRuleTopicRuleActionRole'),
'Arn',
],
},
ServiceName: expectedAuth.serviceName,
SigningRegion: expectedAuth.signingRegion,
},
},
},
},
],
},
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "31.0.0",
"files": {
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
"source": {
"path": "IoTHttpsActionDefaultTestDeployAssert019947CA.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
},
"Rules": {
"CheckBootstrapVersion": {
"Assertions": [
{
"Assert": {
"Fn::Not": [
{
"Fn::Contains": [
[
"1",
"2",
"3",
"4",
"5"
],
{
"Ref": "BootstrapVersion"
}
]
}
]
},
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "31.0.0",
"files": {
"ceb4c33f2a2723481892f941c88433106a7019ed281d49385fdbaf9f0b09a343": {
"source": {
"path": "IoTHttpsActionTestStack.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "ceb4c33f2a2723481892f941c88433106a7019ed281d49385fdbaf9f0b09a343.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Loading