Skip to content

Commit 95cb3f3

Browse files
authored
feat(iotevents): support comparison operators (#19329)
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*
1 parent cfb140e commit 95cb3f3

File tree

3 files changed

+87
-104
lines changed

3 files changed

+87
-104
lines changed

packages/@aws-cdk/aws-iotevents/README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,20 @@ const coldState = new iotevents.State({
8181
stateName: 'cold',
8282
});
8383

84-
// transit to coldState when temperature is 10
84+
// transit to coldState when temperature is less than 15
8585
warmState.transitionTo(coldState, {
8686
eventName: 'to_coldState', // optional property, default by combining the names of the States
87-
when: iotevents.Expression.eq(
87+
when: iotevents.Expression.lt(
8888
iotevents.Expression.inputAttribute(input, 'payload.temperature'),
89-
iotevents.Expression.fromString('10'),
89+
iotevents.Expression.fromString('15'),
9090
),
9191
executing: [new actions.LambdaInvokeAction(func)], // optional
9292
});
93-
// transit to warmState when temperature is 20
93+
// transit to warmState when temperature is greater than or equal to 15
9494
coldState.transitionTo(warmState, {
95-
when: iotevents.Expression.eq(
95+
when: iotevents.Expression.gte(
9696
iotevents.Expression.inputAttribute(input, 'payload.temperature'),
97-
iotevents.Expression.fromString('20'),
97+
iotevents.Expression.fromString('15'),
9898
),
9999
});
100100

packages/@aws-cdk/aws-iotevents/lib/expression.ts

+62-5
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,71 @@ export abstract class Expression {
3131
* Create a expression for the Equal operator.
3232
*/
3333
public static eq(left: Expression, right: Expression): Expression {
34-
return new BinaryOperationExpression(left, '==', right);
34+
return new BinaryOperationExpression(left, '==', right, 9);
35+
}
36+
37+
/**
38+
* Create a expression for the Not Equal operator.
39+
*/
40+
public static neq(left: Expression, right: Expression): Expression {
41+
return new BinaryOperationExpression(left, '!=', right, 9);
42+
}
43+
44+
/**
45+
* Create a expression for the Less Than operator.
46+
*/
47+
public static lt(left: Expression, right: Expression): Expression {
48+
return new BinaryOperationExpression(left, '<', right, 10);
49+
}
50+
51+
/**
52+
* Create a expression for the Less Than Or Equal operator.
53+
*/
54+
public static lte(left: Expression, right: Expression): Expression {
55+
return new BinaryOperationExpression(left, '<=', right, 10);
56+
}
57+
58+
/**
59+
* Create a expression for the Greater Than operator.
60+
*/
61+
public static gt(left: Expression, right: Expression): Expression {
62+
return new BinaryOperationExpression(left, '>', right, 10);
63+
}
64+
65+
/**
66+
* Create a expression for the Greater Than Or Equal operator.
67+
*/
68+
public static gte(left: Expression, right: Expression): Expression {
69+
return new BinaryOperationExpression(left, '>=', right, 10);
3570
}
3671

3772
/**
3873
* Create a expression for the AND operator.
3974
*/
4075
public static and(left: Expression, right: Expression): Expression {
41-
return new BinaryOperationExpression(left, '&&', right);
76+
return new BinaryOperationExpression(left, '&&', right, 5);
77+
}
78+
79+
/**
80+
* Create a expression for the OR operator.
81+
*/
82+
public static or(left: Expression, right: Expression): Expression {
83+
return new BinaryOperationExpression(left, '||', right, 4);
4284
}
4385

4486
constructor() {
4587
}
4688

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

53101
class StringExpression extends Expression {
@@ -65,11 +113,20 @@ class BinaryOperationExpression extends Expression {
65113
private readonly left: Expression,
66114
private readonly operator: string,
67115
private readonly right: Expression,
116+
/**
117+
* Indicates the priority of the operator.
118+
* This is intended to be set according to MDN rules.
119+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table
120+
*/
121+
private readonly priority: number,
68122
) {
69123
super();
70124
}
71125

72-
public evaluate() {
73-
return `${this.left.evaluate()} ${this.operator} ${this.right.evaluate()}`;
126+
public evaluate(parentPriority?: number) {
127+
const expression = `${this.left.evaluate(this.priority)} ${this.operator} ${this.right.evaluate(this.priority)}`;
128+
return parentPriority === undefined || parentPriority <= this.priority
129+
? expression
130+
: `(${expression})`;
74131
}
75132
}

packages/@aws-cdk/aws-iotevents/test/detector-model.test.ts

+19-93
Original file line numberDiff line numberDiff line change
@@ -498,14 +498,30 @@ test('cannot create transitions that transit to duprecated target state', () =>
498498
});
499499

500500
describe('Expression', () => {
501-
test('currentInput', () => {
501+
const E = iotevents.Expression;
502+
test.each([
503+
['currentInput', (testInput: iotevents.IInput) => E.currentInput(testInput), 'currentInput("test-input")'],
504+
['inputAttribute', (testInput: iotevents.IInput) => E.inputAttribute(testInput, 'json.path'), '$input.test-input.json.path'],
505+
['eq', () => E.eq(E.fromString('"aaa"'), E.fromString('"bbb"')), '"aaa" == "bbb"'],
506+
['neq', () => E.neq(E.fromString('"aaa"'), E.fromString('"bbb"')), '"aaa" != "bbb"'],
507+
['lt', () => E.lt(E.fromString('5'), E.fromString('2')), '5 < 2'],
508+
['lte', () => E.lte(E.fromString('5'), E.fromString('2')), '5 <= 2'],
509+
['gt', () => E.gt(E.fromString('5'), E.fromString('2')), '5 > 2'],
510+
['gte', () => E.gte(E.fromString('5'), E.fromString('2')), '5 >= 2'],
511+
['and', () => E.and(E.fromString('true'), E.fromString('false')), 'true && false'],
512+
['or', () => E.or(E.fromString('true'), E.fromString('false')), 'true || false'],
513+
['operator priority', () => E.and(
514+
E.and(E.fromString('false'), E.fromString('false')),
515+
E.or(E.fromString('true'), E.fromString('true')),
516+
), 'false && false && (true || true)'],
517+
])('%s', (_, getExpression, expectedCondition) => {
502518
// WHEN
503519
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
504520
initialState: new iotevents.State({
505521
stateName: 'test-state',
506522
onEnter: [{
507523
eventName: 'test-eventName',
508-
condition: iotevents.Expression.currentInput(input),
524+
condition: getExpression(input),
509525
}],
510526
}),
511527
});
@@ -517,97 +533,7 @@ describe('Expression', () => {
517533
Match.objectLike({
518534
OnEnter: {
519535
Events: [Match.objectLike({
520-
Condition: 'currentInput("test-input")',
521-
})],
522-
},
523-
}),
524-
],
525-
},
526-
});
527-
});
528-
529-
test('inputAttribute', () => {
530-
// WHEN
531-
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
532-
initialState: new iotevents.State({
533-
stateName: 'test-state',
534-
onEnter: [{
535-
eventName: 'test-eventName',
536-
condition: iotevents.Expression.inputAttribute(input, 'json.path'),
537-
}],
538-
}),
539-
});
540-
541-
// THEN
542-
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
543-
DetectorModelDefinition: {
544-
States: [
545-
Match.objectLike({
546-
OnEnter: {
547-
Events: [Match.objectLike({
548-
Condition: '$input.test-input.json.path',
549-
})],
550-
},
551-
}),
552-
],
553-
},
554-
});
555-
});
556-
557-
test('eq', () => {
558-
// WHEN
559-
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
560-
initialState: new iotevents.State({
561-
stateName: 'test-state',
562-
onEnter: [{
563-
eventName: 'test-eventName',
564-
condition: iotevents.Expression.eq(
565-
iotevents.Expression.fromString('"aaa"'),
566-
iotevents.Expression.fromString('"bbb"'),
567-
),
568-
}],
569-
}),
570-
});
571-
572-
// THEN
573-
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
574-
DetectorModelDefinition: {
575-
States: [
576-
Match.objectLike({
577-
OnEnter: {
578-
Events: [Match.objectLike({
579-
Condition: '"aaa" == "bbb"',
580-
})],
581-
},
582-
}),
583-
],
584-
},
585-
});
586-
});
587-
588-
test('eq', () => {
589-
// WHEN
590-
new iotevents.DetectorModel(stack, 'MyDetectorModel', {
591-
initialState: new iotevents.State({
592-
stateName: 'test-state',
593-
onEnter: [{
594-
eventName: 'test-eventName',
595-
condition: iotevents.Expression.and(
596-
iotevents.Expression.fromString('true'),
597-
iotevents.Expression.fromString('false'),
598-
),
599-
}],
600-
}),
601-
});
602-
603-
// THEN
604-
Template.fromStack(stack).hasResourceProperties('AWS::IoTEvents::DetectorModel', {
605-
DetectorModelDefinition: {
606-
States: [
607-
Match.objectLike({
608-
OnEnter: {
609-
Events: [Match.objectLike({
610-
Condition: 'true && false',
536+
Condition: expectedCondition,
611537
})],
612538
},
613539
}),

0 commit comments

Comments
 (0)