From b4ee0cbd8bd944fb1c93e7f027b9071778ab0afc Mon Sep 17 00:00:00 2001 From: Tatsuya Yamamoto Date: Wed, 9 Mar 2022 05:12:42 +0900 Subject: [PATCH] feat(iotevents): support setting Events on input and exit for State (#19249) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR allow states in IoT Events detector model to set event on input and exit. This PR is in roadmap of https://github.com/aws/aws-cdk/issues/17711. スクリーンショット 2022-03-05 13 40 57 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../integ.lambda-invoke-action.expected.json | 1 - packages/@aws-cdk/aws-iotevents/README.md | 10 ++++- packages/@aws-cdk/aws-iotevents/lib/state.ts | 40 ++++++++++++++----- .../aws-iotevents/test/detector-model.test.ts | 20 ++++++++++ .../test/integ.detector-model.expected.json | 36 +++++++++++++++++ .../test/integ.detector-model.ts | 14 +++++++ 6 files changed, 110 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/aws-iotevents-actions/test/lambda/integ.lambda-invoke-action.expected.json b/packages/@aws-cdk/aws-iotevents-actions/test/lambda/integ.lambda-invoke-action.expected.json index be70d65360d32..7e5d5b881d01d 100644 --- a/packages/@aws-cdk/aws-iotevents-actions/test/lambda/integ.lambda-invoke-action.expected.json +++ b/packages/@aws-cdk/aws-iotevents-actions/test/lambda/integ.lambda-invoke-action.expected.json @@ -144,7 +144,6 @@ } ] }, - "OnInput": {}, "StateName": "MyState" } ] diff --git a/packages/@aws-cdk/aws-iotevents/README.md b/packages/@aws-cdk/aws-iotevents/README.md index 0c7e491c65fae..9089fdec07cbd 100644 --- a/packages/@aws-cdk/aws-iotevents/README.md +++ b/packages/@aws-cdk/aws-iotevents/README.md @@ -64,10 +64,18 @@ const input = new iotevents.Input(this, 'MyInput', { const warmState = new iotevents.State({ stateName: 'warm', onEnter: [{ - eventName: 'test-event', + eventName: 'test-enter-event', condition: iotevents.Expression.currentInput(input), actions: [new actions.LambdaInvokeAction(func)], // optional }], + onInput: [{ // optional + eventName: 'test-input-event', + actions: [new actions.LambdaInvokeAction(func)], + }], + onExit: [{ // optional + eventName: 'test-exit-event', + actions: [new actions.LambdaInvokeAction(func)], + }], }); const coldState = new iotevents.State({ stateName: 'cold', diff --git a/packages/@aws-cdk/aws-iotevents/lib/state.ts b/packages/@aws-cdk/aws-iotevents/lib/state.ts index 0159628c4a4ff..ef8ce689fec47 100644 --- a/packages/@aws-cdk/aws-iotevents/lib/state.ts +++ b/packages/@aws-cdk/aws-iotevents/lib/state.ts @@ -66,12 +66,28 @@ export interface StateProps { readonly stateName: string; /** - * Specifies the events on enter. the conditions of the events are evaluated when the state is entered. - * If the condition is `TRUE`, the actions of the event are performed. + * Specifies the events on enter. The conditions of the events will be evaluated when entering this state. + * If the condition of the event evaluates to `true`, the actions of the event will be executed. * - * @default - events on enter will not be set + * @default - no events will trigger on entering this state */ readonly onEnter?: Event[]; + + /** + * Specifies the events on input. The conditions of the events will be evaluated when any input is received. + * If the condition of the event evaluates to `true`, the actions of the event will be executed. + * + * @default - no events will trigger on input in this state + */ + readonly onInput?: Event[]; + + /** + * Specifies the events on exit. The conditions of the events are evaluated when an exiting this state. + * If the condition evaluates to `true`, the actions of the event will be executed. + * + * @default - no events will trigger on exiting this state + */ + readonly onExit?: Event[]; } /** @@ -141,12 +157,18 @@ export class State { } private toStateJson(scope: Construct, actionBindOptions: ActionBindOptions): CfnDetectorModel.StateProperty { - const { onEnter } = this.props; + const { onEnter, onInput, onExit } = this.props; return { stateName: this.stateName, - onEnter: onEnter && { events: toEventsJson(scope, actionBindOptions, onEnter) }, - onInput: { + onEnter: onEnter && { + events: toEventsJson(scope, actionBindOptions, onEnter), + }, + onInput: (onInput || this.transitionEvents.length !== 0) ? { + events: toEventsJson(scope, actionBindOptions, onInput), transitionEvents: toTransitionEventsJson(scope, actionBindOptions, this.transitionEvents), + } : undefined, + onExit: onExit && { + events: toEventsJson(scope, actionBindOptions, onExit), }, }; } @@ -155,9 +177,9 @@ export class State { function toEventsJson( scope: Construct, actionBindOptions: ActionBindOptions, - events: Event[], -): CfnDetectorModel.EventProperty[] { - return events.map(event => ({ + events?: Event[], +): CfnDetectorModel.EventProperty[] | undefined { + return events?.map(event => ({ eventName: event.eventName, condition: event.condition?.evaluate(), actions: event.actions?.map(action => action.bind(scope, actionBindOptions).configuration), diff --git a/packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts b/packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts index ec754f25c1aad..e3844ce7915ba 100644 --- a/packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts +++ b/packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts @@ -176,6 +176,26 @@ test('can set actions to events', () => { }); }); +test.each([ + ['onInput', { onInput: [{ eventName: 'test-eventName1' }] }, { OnInput: { Events: [{ EventName: 'test-eventName1' }] } }], + ['onExit', { onExit: [{ eventName: 'test-eventName1' }] }, { OnExit: { Events: [{ EventName: 'test-eventName1' }] } }], +])('can set %s to State', (_, events, expected) => { + // WHEN + new iotevents.DetectorModel(stack, 'MyDetectorModel', { + initialState: new iotevents.State({ + stateName: 'test-state', + onEnter: [{ eventName: 'test-eventName1', condition: iotevents.Expression.currentInput(input) }], + ...events, + }), + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', { + DetectorModelDefinition: { + States: [Match.objectLike(expected)], + }, + }); +}); test('can set an action to multiple detector models', () => { // GIVEN an action diff --git a/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.expected.json b/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.expected.json index 888869a41e68e..3b24a7f7776d8 100644 --- a/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.expected.json +++ b/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.expected.json @@ -63,7 +63,43 @@ } ] }, + "OnExit": { + "Events": [ + { + "Condition": { + "Fn::Join": [ + "", + [ + "$input.", + { + "Ref": "MyInput08947B23" + }, + ".payload.temperature == 31.7" + ] + ] + }, + "EventName": "test-exit-event" + } + ] + }, "OnInput": { + "Events": [ + { + "Condition": { + "Fn::Join": [ + "", + [ + "$input.", + { + "Ref": "MyInput08947B23" + }, + ".payload.temperature == 31.6" + ] + ] + }, + "EventName": "test-input-event" + } + ], "TransitionEvents": [ { "Condition": { diff --git a/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts b/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts index 5f6d2839f3a93..1c3996b226623 100644 --- a/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts +++ b/packages/@aws-cdk/aws-iotevents/test/integ.detector-model.ts @@ -23,6 +23,20 @@ class TestStack extends cdk.Stack { ), ), }], + onInput: [{ + eventName: 'test-input-event', + condition: iotevents.Expression.eq( + iotevents.Expression.inputAttribute(input, 'payload.temperature'), + iotevents.Expression.fromString('31.6'), + ), + }], + onExit: [{ + eventName: 'test-exit-event', + condition: iotevents.Expression.eq( + iotevents.Expression.inputAttribute(input, 'payload.temperature'), + iotevents.Expression.fromString('31.7'), + ), + }], }); const offlineState = new iotevents.State({ stateName: 'offline',