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(iotevents): add DetectorModel L2 Construct #18049

Merged
merged 26 commits into from
Jan 22, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e5f18b2
feat(iotevents): add DetectorModel L2 Construct
yamatatsu Dec 16, 2021
7873d17
cut out the event.
yamatatsu Dec 16, 2021
07081ed
fix readme
yamatatsu Dec 16, 2021
3d2db07
address comments
yamatatsu Dec 17, 2021
9c24a19
Merge branch 'master' into iotevents-dm
yamatatsu Jan 5, 2022
0723a1c
implement Expressions
yamatatsu Jan 19, 2022
8f82cbb
Update packages/@aws-cdk/aws-iotevents/README.md
yamatatsu Jan 21, 2022
fceea45
Update packages/@aws-cdk/aws-iotevents/README.md
yamatatsu Jan 21, 2022
8ae95cd
Update packages/@aws-cdk/aws-iotevents/lib/detector-model.ts
yamatatsu Jan 21, 2022
ff69d77
Update packages/@aws-cdk/aws-iotevents/lib/detector-model.ts
yamatatsu Jan 21, 2022
d197256
Update packages/@aws-cdk/aws-iotevents/lib/event.ts
yamatatsu Jan 21, 2022
f3511b2
Update packages/@aws-cdk/aws-iotevents/lib/event.ts
yamatatsu Jan 21, 2022
e5baa25
Update packages/@aws-cdk/aws-iotevents/lib/detector-model.ts
yamatatsu Jan 21, 2022
581ddef
Update packages/@aws-cdk/aws-iotevents/lib/state.ts
yamatatsu Jan 21, 2022
50c183c
Update packages/@aws-cdk/aws-iotevents/lib/detector-model.ts
yamatatsu Jan 21, 2022
971d5c4
Update packages/@aws-cdk/aws-iotevents/lib/detector-model.ts
yamatatsu Jan 21, 2022
fbfe028
Update packages/@aws-cdk/aws-iotevents/lib/detector-model.ts
yamatatsu Jan 21, 2022
0b6600d
Update packages/@aws-cdk/aws-iotevents/lib/expression.ts
yamatatsu Jan 21, 2022
18e86d0
Update packages/@aws-cdk/aws-iotevents/lib/state.ts
yamatatsu Jan 21, 2022
9744d78
Update packages/@aws-cdk/aws-iotevents/lib/state.ts
yamatatsu Jan 21, 2022
d89d0a0
fix tests
yamatatsu Jan 21, 2022
6ff671e
address comments
yamatatsu Jan 21, 2022
78fc88c
fix JSDoc of onEnter and rename
yamatatsu Jan 21, 2022
5506cb5
Update packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts
yamatatsu Jan 21, 2022
087bb42
address comments
yamatatsu Jan 21, 2022
2b4301e
Merge branch 'master' into iotevents-dm
mergify[bot] Jan 22, 2022
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
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 model to your stack.
The detector model need a reference to at least one AWS IoT Events input.
AWS IoT Events inputs enable the detector to 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',
onEnter: [{
eventName: 'test-event',
condition: iotevents.Expression.currentInput(input),
}],
});

new iotevents.DetectorModel(this, 'MyDetectorModel', {
detectorModelName: 'test-detector-model', // optional
initialState: onlineState,
});
```
82 changes: 82 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,82 @@
import * as iam from '@aws-cdk/aws-iam';
import { Resource, IResource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnDetectorModel } from './iotevents.generated';
import { State } from './state';

/**
* Represents an AWS IoT Events detector model
*/
export interface IDetectorModel extends IResource {
/**
* The name of the detector model.
*
* @attribute
*/
readonly detectorModelName: string;
}

/**
* 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;

/**
* The role that grants permission to AWS IoT Events to perform its operations.
*
* @default - a role will be created with default permissions
*/
readonly role?: iam.IRole;
}

yamatatsu marked this conversation as resolved.
Show resolved Hide resolved
/**
* Defines an AWS IoT Events detector model in this stack.
*/
export class DetectorModel extends Resource implements IDetectorModel {
/**
* Import an existing detector model.
*/
public static fromDetectorModelName(scope: Construct, id: string, detectorModelName: string): IDetectorModel {
return new class extends Resource implements IDetectorModel {
public readonly detectorModelName = detectorModelName;
}(scope, id);
}

public readonly detectorModelName: string;

constructor(scope: Construct, id: string, props: DetectorModelProps) {
super(scope, id, {
physicalName: props.detectorModelName,
});

if (!props.initialState._onEnterEventsHaveAtLeastOneCondition()) {
throw new Error('Detector Model must have at least one Input with a condition');
}

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

const resource = new CfnDetectorModel(this, 'Resource', {
detectorModelName: this.physicalName,
detectorModelDefinition: {
initialStateName: props.initialState.stateName,
states: [props.initialState._toStateJson()],
},
roleArn: role.roleArn,
});

this.detectorModelName = this.getResourceNameAttribute(resource.ref);
}
}
18 changes: 18 additions & 0 deletions packages/@aws-cdk/aws-iotevents/lib/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Expression } from './expression';

/**
* Specifies the actions to be performed when the condition evaluates to TRUE.
skinny85 marked this conversation as resolved.
Show resolved Hide resolved
*/
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 (the actions are always executed)
*/
readonly condition?: Expression;
}
75 changes: 75 additions & 0 deletions packages/@aws-cdk/aws-iotevents/lib/expression.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { IInput } from './input';

/**
* Expression for events in Detector Model state
* @see https://docs.aws.amazon.com/iotevents/latest/developerguide/iotevents-expressions.html
*/
export abstract class Expression {
yamatatsu marked this conversation as resolved.
Show resolved Hide resolved
/**
* Create a expression from the given string
*/
public static fromString(value: string): Expression {
return new StringExpression(value);
}

/**
* Create a expression for function `currentInput()`.
* It is evaluated to true if the specified input message was received.
*/
public static currentInput(input: IInput): Expression {
return this.fromString(`currentInput("${input.inputName}")`);
}
yamatatsu marked this conversation as resolved.
Show resolved Hide resolved

/**
* Create a expression for get an input attribute as `$input.TemperatureInput.temperatures[2]`.
*/
public static inputAttribute(input: IInput, path: string): Expression {
return this.fromString(`$input.${input.inputName}.${path}`);
}

/**
* Create a expression for the Equal operator
*/
public static eq(left: Expression, right: Expression): Expression {
return new BinaryOperationExpression(left, '==', right);
}

/**
* Create a expression for the AND operator
*/
public static and(left: Expression, right: Expression): Expression {
return new BinaryOperationExpression(left, '&&', right);
}

constructor() {
}

/**
* this is called to evaluate the expression
*/
public abstract evaluate(): string;
}

class StringExpression extends Expression {
constructor(private readonly value: string) {
super();
}

public evaluate() {
return this.value;
}
}

class BinaryOperationExpression extends Expression {
constructor(
private readonly left: Expression,
private readonly operator: string,
private readonly right: Expression,
) {
super();
}

public evaluate() {
return `${this.left.evaluate()} ${this.operator} ${this.right.evaluate()}`;
}
}
4 changes: 4 additions & 0 deletions packages/@aws-cdk/aws-iotevents/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export * from './detector-model';
export * from './event';
export * from './expression';
export * from './input';
export * from './state';

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

/**
* Properties for defining a state of a detector
*/
export interface StateProps {
/**
* The name of the state.
*/
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.
*
* @default - events on enter will not be set
*/
readonly onEnter?: 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
*
* @internal
*/
public _toStateJson(): CfnDetectorModel.StateProperty {
const { stateName, onEnter } = this.props;
return {
stateName,
onEnter: onEnter && { events: getEventJson(onEnter) },
};
}

/**
* returns true if this state has at least one condition via events
*
* @internal
*/
public _onEnterEventsHaveAtLeastOneCondition(): boolean {
return this.props.onEnter?.some(event => event.condition) ?? false;
}
}

function getEventJson(events: Event[]): CfnDetectorModel.EventProperty[] {
return events.map(e => {
return {
eventName: e.eventName,
condition: e.condition?.evaluate(),
};
});
}
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 @@ -83,10 +83,12 @@
"jest": "^27.4.5"
},
"dependencies": {
"@aws-cdk/aws-iam": "0.0.0",
yamatatsu marked this conversation as resolved.
Show resolved Hide resolved
"@aws-cdk/core": "0.0.0",
"constructs": "^3.3.69"
},
"peerDependencies": {
"@aws-cdk/aws-iam": "0.0.0",
yamatatsu marked this conversation as resolved.
Show resolved Hide resolved
"@aws-cdk/core": "0.0.0",
"constructs": "^3.3.69"
},
Expand Down
Loading