Skip to content

Commit

Permalink
feat(iotevents): Support the timer actions
Browse files Browse the repository at this point in the history
  • Loading branch information
yamatatsu committed Jul 31, 2022
1 parent 0ea8a1c commit 1af03c9
Show file tree
Hide file tree
Showing 19 changed files with 890 additions and 0 deletions.
39 changes: 39 additions & 0 deletions packages/@aws-cdk/aws-iotevents-actions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,48 @@ AWS IoT Events can trigger actions when it detects a specified event or transiti

Currently supported are:

- Use timer
- Set variable to detector instanse
- Invoke a Lambda function

## Use timer

The code snippet below creates an Action that creates the timer a timer with duration in seconds.

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

declare const input: iotevents.IInput;

const state = new iotevents.State({
stateName: 'MyState',
onEnter: [{
eventName: 'test-event',
condition: iotevents.Expression.currentInput(input),
actions: [
actions: [
new actions.SetTimerAction('MyTimer', {
duration: cdk.Duration.seconds(60),
}),
],
],
}],
});
```

Also you can set duration by [IoT Events Expression](https://docs.aws.amazon.com/iotevents/latest/developerguide/iotevents-expressions.html) as following:

```ts
new actions.SetTimerAction('MyTimer', {
durationExpression: iotevents.Expression.inputAttribute(input, 'payload.durationSeconds'),
})
```

And the timer can be reseted and cleared as below example.

[Using the timer actions example](test/iot/integ.timer-actions.ts).

## Set variable to detector instanse

The code snippet below creates an Action that set variable to detector instanse
Expand Down
22 changes: 22 additions & 0 deletions packages/@aws-cdk/aws-iotevents-actions/lib/clear-timer-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as iotevents from '@aws-cdk/aws-iotevents';
import { Construct } from 'constructs';

/**
* The action to delete an existing timer.
*/
export class ClearTimerAction implements iotevents.IAction {
/**
* @param timerName the name of the timer
*/
constructor(private readonly timerName: string) {}

bind(_scope: Construct, _options: iotevents.ActionBindOptions): iotevents.ActionConfig {
return {
configuration: {
clearTimer: {
timerName: this.timerName,
},
},
};
}
}
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-iotevents-actions/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
export * from './clear-timer-action';
export * from './set-variable-action';
export * from './lambda-invoke-action';
export * from './reset-timer-action';
export * from './set-timer-action';
22 changes: 22 additions & 0 deletions packages/@aws-cdk/aws-iotevents-actions/lib/reset-timer-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as iotevents from '@aws-cdk/aws-iotevents';
import { Construct } from 'constructs';

/**
* The action to reset an existing timer.
*/
export class ResetTimerAction implements iotevents.IAction {
/**
* @param timerName the name of the timer
*/
constructor(private readonly timerName: string) {}

bind(_scope: Construct, _options: iotevents.ActionBindOptions): iotevents.ActionConfig {
return {
configuration: {
resetTimer: {
timerName: this.timerName,
},
},
};
}
}
77 changes: 77 additions & 0 deletions packages/@aws-cdk/aws-iotevents-actions/lib/set-timer-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as iotevents from '@aws-cdk/aws-iotevents';
import * as cdk from '@aws-cdk/core';
import { Construct } from 'constructs';

/**
* Configuration properties of an action to set a timer.
*/
export interface SetTimerActionProps {
/**
* The duration of the timer, in seconds. One of `duration` or `durationExpression` is required.
*
* The range of the duration is 60-31622400 seconds.
* The evaluated result of the duration expression is rounded down to the nearest whole number.
* For example, if you set the timer to 60.99 seconds, the evaluated result of the duration expression is 60 seconds.
*
* @default - none, required if no `durationExpression` is defined.
*/
readonly duration?: cdk.Duration;

/**
* The duration of the timer, in seconds. One of `duration` or `durationExpression` is required.
*
* You can use a string expression that includes numbers, variables ($variable.<variable-name>),
* and input values ($input.<input-name>.<path-to-datum>) as the duration.
*
* The range of the duration is 60-31622400 seconds.
* The evaluated result of the duration expression is rounded down to the nearest whole number.
* For example, if you set the timer to 60.99 seconds, the evaluated result of the duration expression is 60 seconds.
*
* @default - none, required if no `duration` is defined.
*/
readonly durationExpression?: iotevents.Expression;
}

/**
* The action to create a timer with duration in seconds.
*/
export class SetTimerAction implements iotevents.IAction {
private readonly durationExpression: string | undefined;

/**
* @param timerName the name of the timer
* @param props the properties to set duration
*/
constructor(private readonly timerName: string, props: SetTimerActionProps) {
if (!props.duration && !props.durationExpression) {
throw new Error('Either duration or durationExpression must be specified');
}
if (props.duration && props.durationExpression) {
throw new Error('duration and durationExpression cannot be specified at the same time');
}
if (props.duration) {
const seconds = props.duration.toSeconds();
if (seconds < 60) {
throw new Error(`duration cannot be less than 60 seconds, got: ${props.duration.toString()}`);
}
if (seconds > 31622400) {
throw new Error(`duration cannot be greater than 31622400 seconds, got: ${props.duration.toString()}`);
}
this.durationExpression = seconds.toString();
}
if (props.durationExpression) {
this.durationExpression = props.durationExpression.evaluate();
}
}

bind(_scope: Construct, _options: iotevents.ActionBindOptions): iotevents.ActionConfig {
return {
configuration: {
setTimer: {
timerName: this.timerName,
durationExpression: this.durationExpression,
},
},
};
}
}
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-iotevents-actions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"@aws-cdk/assertions": "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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Template } from '@aws-cdk/assertions';
import * as iotevents from '@aws-cdk/aws-iotevents';
import * as cdk from '@aws-cdk/core';
import * as actions from '../../lib';

let stack: cdk.Stack;
let input: iotevents.IInput;
beforeEach(() => {
stack = new cdk.Stack();
input = iotevents.Input.fromInputName(stack, 'MyInput', 'test-input');
});

test('Default property', () => {
// WHEN
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
initialState: new iotevents.State({
stateName: 'test-state',
onEnter: [{
eventName: 'test-eventName',
condition: iotevents.Expression.currentInput(input),
actions: [
new actions.ClearTimerAction('MyTimer'),
],
}],
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
DetectorModelDefinition: {
States: [{
OnEnter: {
Events: [{
Actions: [{
ClearTimer: {
TimerName: 'MyTimer',
},
}],
}],
},
}],
},
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Stack verification steps:
* * put a message
* * aws iotevents-data batch-put-message --region=us-east-1 --messages=messageId=(date | md5),inputName=test_input,payload=(echo '{"payload":{"deviceId":"000"}}' | base64)
*/
import * as iotevents from '@aws-cdk/aws-iotevents';
import * as cdk from '@aws-cdk/core';
import { IntegTest } from '@aws-cdk/integ-tests';
import * as actions from '../../lib';

/**
* This example will creates the detector model for Device HeartBeat Monitoring.
*
* @see https://docs.aws.amazon.com/iotevents/latest/developerguide/iotevents-examples-dhb.html
*/
class TestStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);

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

const online = new iotevents.State({
stateName: 'Online',
onEnter: [{
eventName: 'enter-event',
condition: iotevents.Expression.currentInput(input),
actions: [
new actions.SetTimerAction('MyTimer', {
duration: cdk.Duration.seconds(60),
}),
],
}],
onInput: [{
eventName: 'input-event',
condition: iotevents.Expression.currentInput(input),
actions: [
new actions.ResetTimerAction('MyTimer'),
],
}],
onExit: [{
eventName: 'exit-event',
actions: [
new actions.ClearTimerAction('MyTimer'),
],
}],
});
const offline = new iotevents.State({ stateName: 'Offline' });

online.transitionTo(offline, { when: iotevents.Expression.timeout('MyTimer') });
offline.transitionTo(online, { when: iotevents.Expression.currentInput(input) });

new iotevents.DetectorModel(this, 'MyDetectorModel', {
detectorKey: 'payload.deviceId',
initialState: online,
});
}
}

// GIVEN
const app = new cdk.App();
const stack = new TestStack(app, 'iotevents-timer-actions-test-stack');
new IntegTest(app, 'TimerActions', { testCases: [stack] });
app.synth();
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Template } from '@aws-cdk/assertions';
import * as iotevents from '@aws-cdk/aws-iotevents';
import * as cdk from '@aws-cdk/core';
import * as actions from '../../lib';

let stack: cdk.Stack;
let input: iotevents.IInput;
beforeEach(() => {
stack = new cdk.Stack();
input = iotevents.Input.fromInputName(stack, 'MyInput', 'test-input');
});

test('Default property', () => {
// WHEN
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
initialState: new iotevents.State({
stateName: 'test-state',
onEnter: [{
eventName: 'test-eventName',
condition: iotevents.Expression.currentInput(input),
actions: [
new actions.ResetTimerAction('MyTimer'),
],
}],
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
DetectorModelDefinition: {
States: [{
OnEnter: {
Events: [{
Actions: [{
ResetTimer: {
TimerName: 'MyTimer',
},
}],
}],
},
}],
},
});
});
Loading

0 comments on commit 1af03c9

Please sign in to comment.