Skip to content

Commit

Permalink
feat(iotevents): add DetectorModel L2 Construct
Browse files Browse the repository at this point in the history
  • Loading branch information
yamatatsu committed Dec 16, 2021
1 parent 465dabf commit e5f18b2
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 7 deletions.
23 changes: 19 additions & 4 deletions packages/@aws-cdk/aws-iotevents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,30 @@ Import it into your code:
import * as iotevents from '@aws-cdk/aws-iotevents';
```

## `Input`
## `DetectorModel`

Add an AWS IoT Events input to your stack:
The following example creates an AWS IoT Events detector dodel to your stack.
The detector must have at least one reference to an AWS IoT Events input.
AWS IoT Events input enable that the detector can get MQTT payload values from IoT Core rules.

```ts
import * as iotevents from '@aws-cdk/aws-iotevents';

new iotevents.Input(this, 'MyInput', {
inputName: 'my_input',
const input = new iotevents.Input(this, 'MyInput', {
inputName: 'my_input', // optional
attributeJsonPaths: ['payload.temperature'],
});

const onlineState = new iotevents.State({
stateName: 'online',
onEnterEvents: [{
eventName: 'test-event',
condition: `currentInput("${input.inputName}")`,
}],
});

new iotevents.DetectorModel(this, 'MyDetectorModel', {
detectorModelName: 'test-detector-model', // optional
initialState: onlineState,
});
```
46 changes: 46 additions & 0 deletions packages/@aws-cdk/aws-iotevents/lib/detector-model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as iam from '@aws-cdk/aws-iam';
import { Resource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnDetectorModel } from './iotevents.generated';
import { State } from './state';

/**
* Properties for defining an AWS IoT Events detector model
*/
export interface DetectorModelProps {
/**
* The name of the detector model
*
* @default - CloudFormation will generate a unique name of the detector model
*/
readonly detectorModelName?: string,

/**
* The state that is entered at the creation of each detector.
*/
readonly initialState: State;
}

/**
* Defines an AWS IoT Events detector model in this stack.
*/
export class DetectorModel extends Resource {
constructor(scope: Construct, id: string, props: DetectorModelProps) {
super(scope, id, {
physicalName: props.detectorModelName,
});

const role = new iam.Role(this, 'DetectorModelRole', {
assumedBy: new iam.ServicePrincipal('iotevents.amazonaws.com'),
});

new CfnDetectorModel(this, 'Resource', {
detectorModelName: this.physicalName,
detectorModelDefinition: {
initialStateName: props.initialState.stateName,
states: [props.initialState.toStateJson()],
},
roleArn: role.roleArn,
});
}
}
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-iotevents/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from './input';
export * from './detector-model';
export * from './state';

// AWS::IoTEvents CloudFormation Resources:
export * from './iotevents.generated';
60 changes: 60 additions & 0 deletions packages/@aws-cdk/aws-iotevents/lib/state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { CfnDetectorModel } from './iotevents.generated';

/**
* Specifies the actions to be performed when the condition evaluates to TRUE.
*/
export interface Event {
/**
* The name of the event
*/
readonly eventName: string;

/**
* The Boolean expression that, when TRUE, causes the actions to be performed.
*
* @default None - Defaults to perform the actions always.
*/
readonly condition?: string;
}

/**
* Properties for defining a state of a detector
*/
export interface StateProps {
/**
* The name of the state
*/
readonly stateName: string

/**
* Specifies the actions that are performed when the state is entered and the `condition` is `TRUE`
*
* @default None
*/
readonly onEnterEvents?: Event[]
}

/**
* Defines a state of a detector
*/
export class State {
/**
* The name of the state
*/
public readonly stateName: string;

constructor(private readonly props: StateProps) {
this.stateName = props.stateName;
}

/**
* Return the state property JSON
*/
public toStateJson(): CfnDetectorModel.StateProperty {
const { stateName, onEnterEvents } = this.props;
return {
stateName,
onEnter: onEnterEvents && { events: onEnterEvents },
};
}
}
2 changes: 2 additions & 0 deletions packages/@aws-cdk/aws-iotevents/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,12 @@
},
"dependencies": {
"@aws-cdk/core": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"constructs": "^3.3.69"
},
"peerDependencies": {
"@aws-cdk/core": "0.0.0",
"@aws-cdk/aws-iam": "0.0.0",
"constructs": "^3.3.69"
},
"engines": {
Expand Down
83 changes: 83 additions & 0 deletions packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Match, Template } from '@aws-cdk/assertions';
import * as cdk from '@aws-cdk/core';
import * as iotevents from '../lib';

test('Default property', () => {
const stack = new cdk.Stack();

// WHEN
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
initialState: new iotevents.State({
stateName: 'test-state',
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
DetectorModelDefinition: {
InitialStateName: 'test-state',
States: [{
StateName: 'test-state',
}],
},
RoleArn: {
'Fn::GetAtt': ['MyDetectorModelDetectorModelRoleF2FB4D88', 'Arn'],
},
});

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

test('can set physical name', () => {
const stack = new cdk.Stack();

// WHEN
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
detectorModelName: 'test-detector-model',
initialState: new iotevents.State({ stateName: 'test-state' }),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
DetectorModelName: 'test-detector-model',
});
});

test('can set onEnterEvents', () => {
const stack = new cdk.Stack();

// WHEN
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
initialState: new iotevents.State({
stateName: 'test-state',
onEnterEvents: [{
eventName: 'test-eventName',
condition: 'test-eventCondition',
}],
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
DetectorModelDefinition: {
States: [
Match.objectLike({
OnEnter: {
Events: [{
EventName: 'test-eventName',
Condition: 'test-eventCondition',
}],
},
}),
],
},
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,63 @@
"JsonPath": "payload.temperature"
}
]
}
}
},
"MyDetectorModelDetectorModelRoleF2FB4D88": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "iotevents.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
},
"MyDetectorModel559C0B0E": {
"Type": "AWS::IoTEvents::DetectorModel",
"Properties": {
"DetectorModelDefinition": {
"InitialStateName": "online",
"States": [
{
"OnEnter": {
"Events": [
{
"Condition": {
"Fn::Join": [
"",
[
"currentInput(\"",
{
"Ref": "MyInput08947B23"
},
"\")"
]
]
},
"EventName": "test-event"
}
]
},
"StateName": "online"
}
]
},
"RoleArn": {
"Fn::GetAtt": [
"MyDetectorModelDetectorModelRoleF2FB4D88",
"Arn"
]
},
"InputName": "test_input"
"DetectorModelName": "test-detector-model"
}
}
}
Expand Down
16 changes: 14 additions & 2 deletions packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,22 @@ class TestStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);

new iotevents.Input(this, 'MyInput', {
inputName: 'test_input',
const input = new iotevents.Input(this, 'MyInput', {
attributeJsonPaths: ['payload.temperature'],
});

const onlineState = new iotevents.State({
stateName: 'online',
onEnterEvents: [{
eventName: 'test-event',
condition: `currentInput("${input.inputName}")`,
}],
});

new iotevents.DetectorModel(this, 'MyDetectorModel', {
detectorModelName: 'test-detector-model',
initialState: onlineState,
});
}
}

Expand Down

0 comments on commit e5f18b2

Please sign in to comment.