Skip to content

Commit

Permalink
feat(iotevents): support comparison operators (aws#19329)
Browse files Browse the repository at this point in the history
This PR includes to implement comparison operators.

- [x] integ test

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
yamatatsu authored and Stephen Potter committed Apr 27, 2022
1 parent bac31f0 commit 64f51ea
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 104 deletions.
12 changes: 6 additions & 6 deletions packages/@aws-cdk/aws-iotevents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,20 @@ const coldState = new iotevents.State({
stateName: 'cold',
});

// transit to coldState when temperature is 10
// transit to coldState when temperature is less than 15
warmState.transitionTo(coldState, {
eventName: 'to_coldState', // optional property, default by combining the names of the States
when: iotevents.Expression.eq(
when: iotevents.Expression.lt(
iotevents.Expression.inputAttribute(input, 'payload.temperature'),
iotevents.Expression.fromString('10'),
iotevents.Expression.fromString('15'),
),
executing: [new actions.LambdaInvokeAction(func)], // optional
});
// transit to warmState when temperature is 20
// transit to warmState when temperature is greater than or equal to 15
coldState.transitionTo(warmState, {
when: iotevents.Expression.eq(
when: iotevents.Expression.gte(
iotevents.Expression.inputAttribute(input, 'payload.temperature'),
iotevents.Expression.fromString('20'),
iotevents.Expression.fromString('15'),
),
});

Expand Down
67 changes: 62 additions & 5 deletions packages/@aws-cdk/aws-iotevents/lib/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,71 @@ export abstract class Expression {
* Create a expression for the Equal operator.
*/
public static eq(left: Expression, right: Expression): Expression {
return new BinaryOperationExpression(left, '==', right);
return new BinaryOperationExpression(left, '==', right, 9);
}

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

/**
* Create a expression for the Less Than operator.
*/
public static lt(left: Expression, right: Expression): Expression {
return new BinaryOperationExpression(left, '<', right, 10);
}

/**
* Create a expression for the Less Than Or Equal operator.
*/
public static lte(left: Expression, right: Expression): Expression {
return new BinaryOperationExpression(left, '<=', right, 10);
}

/**
* Create a expression for the Greater Than operator.
*/
public static gt(left: Expression, right: Expression): Expression {
return new BinaryOperationExpression(left, '>', right, 10);
}

/**
* Create a expression for the Greater Than Or Equal operator.
*/
public static gte(left: Expression, right: Expression): Expression {
return new BinaryOperationExpression(left, '>=', right, 10);
}

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

/**
* Create a expression for the OR operator.
*/
public static or(left: Expression, right: Expression): Expression {
return new BinaryOperationExpression(left, '||', right, 4);
}

constructor() {
}

/**
* This is called to evaluate the expression.
*
* @param parentPriority priority of the parent of this expression,
* used for determining whether or not to add parenthesis around the expression.
* This is intended to be set according to MDN rules, see
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table
* for details
*/
public abstract evaluate(): string;
public abstract evaluate(parentPriority?: number): string;
}

class StringExpression extends Expression {
Expand All @@ -65,11 +113,20 @@ class BinaryOperationExpression extends Expression {
private readonly left: Expression,
private readonly operator: string,
private readonly right: Expression,
/**
* Indicates the priority of the operator.
* This is intended to be set according to MDN rules.
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table
*/
private readonly priority: number,
) {
super();
}

public evaluate() {
return `${this.left.evaluate()} ${this.operator} ${this.right.evaluate()}`;
public evaluate(parentPriority?: number) {
const expression = `${this.left.evaluate(this.priority)} ${this.operator} ${this.right.evaluate(this.priority)}`;
return parentPriority === undefined || parentPriority <= this.priority
? expression
: `(${expression})`;
}
}
112 changes: 19 additions & 93 deletions packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,14 +498,30 @@ test('cannot create transitions that transit to duprecated target state', () =>
});

describe('Expression', () => {
test('currentInput', () => {
const E = iotevents.Expression;
test.each([
['currentInput', (testInput: iotevents.IInput) => E.currentInput(testInput), 'currentInput("test-input")'],
['inputAttribute', (testInput: iotevents.IInput) => E.inputAttribute(testInput, 'json.path'), '$input.test-input.json.path'],
['eq', () => E.eq(E.fromString('"aaa"'), E.fromString('"bbb"')), '"aaa" == "bbb"'],
['neq', () => E.neq(E.fromString('"aaa"'), E.fromString('"bbb"')), '"aaa" != "bbb"'],
['lt', () => E.lt(E.fromString('5'), E.fromString('2')), '5 < 2'],
['lte', () => E.lte(E.fromString('5'), E.fromString('2')), '5 <= 2'],
['gt', () => E.gt(E.fromString('5'), E.fromString('2')), '5 > 2'],
['gte', () => E.gte(E.fromString('5'), E.fromString('2')), '5 >= 2'],
['and', () => E.and(E.fromString('true'), E.fromString('false')), 'true && false'],
['or', () => E.or(E.fromString('true'), E.fromString('false')), 'true || false'],
['operator priority', () => E.and(
E.and(E.fromString('false'), E.fromString('false')),
E.or(E.fromString('true'), E.fromString('true')),
), 'false && false && (true || true)'],
])('%s', (_, getExpression, expectedCondition) => {
// WHEN
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
initialState: new iotevents.State({
stateName: 'test-state',
onEnter: [{
eventName: 'test-eventName',
condition: iotevents.Expression.currentInput(input),
condition: getExpression(input),
}],
}),
});
Expand All @@ -517,97 +533,7 @@ describe('Expression', () => {
Match.objectLike({
OnEnter: {
Events: [Match.objectLike({
Condition: 'currentInput("test-input")',
})],
},
}),
],
},
});
});

test('inputAttribute', () => {
// WHEN
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
initialState: new iotevents.State({
stateName: 'test-state',
onEnter: [{
eventName: 'test-eventName',
condition: iotevents.Expression.inputAttribute(input, 'json.path'),
}],
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
DetectorModelDefinition: {
States: [
Match.objectLike({
OnEnter: {
Events: [Match.objectLike({
Condition: '$input.test-input.json.path',
})],
},
}),
],
},
});
});

test('eq', () => {
// WHEN
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
initialState: new iotevents.State({
stateName: 'test-state',
onEnter: [{
eventName: 'test-eventName',
condition: iotevents.Expression.eq(
iotevents.Expression.fromString('"aaa"'),
iotevents.Expression.fromString('"bbb"'),
),
}],
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
DetectorModelDefinition: {
States: [
Match.objectLike({
OnEnter: {
Events: [Match.objectLike({
Condition: '"aaa" == "bbb"',
})],
},
}),
],
},
});
});

test('eq', () => {
// WHEN
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
initialState: new iotevents.State({
stateName: 'test-state',
onEnter: [{
eventName: 'test-eventName',
condition: iotevents.Expression.and(
iotevents.Expression.fromString('true'),
iotevents.Expression.fromString('false'),
),
}],
}),
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
DetectorModelDefinition: {
States: [
Match.objectLike({
OnEnter: {
Events: [Match.objectLike({
Condition: 'true && false',
Condition: expectedCondition,
})],
},
}),
Expand Down

0 comments on commit 64f51ea

Please sign in to comment.