Skip to content

Commit 2e8b2ba

Browse files
authored
Merge pull request #316 from Ficodes/feature/optionalRuleName
Feature/optional rule name
2 parents 9c6ae19 + 0917cbd commit 2e8b2ba

File tree

8 files changed

+144
-12
lines changed

8 files changed

+144
-12
lines changed

CHANGES_NEXT_RELEASE

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
- Remove: old unused development dependencies
88
* grunt and grunt related module
99
* closure-linter-wrapper
10+
- Add 'ruleName' as variable automatically in rule text field (EPL) on rule creation time (#307)
1011
- Change on the PERSEO_ORION_URL env var behaviour. Now it represents Context Broker base URL instead of the
1112
updateContext endpoint
1213
- Add: new ngsijs ~1.2.0 dependency
1314
- Add: new rewire ~4.0.1 dev dependency
1415
- Add: NGSIv2 support in both notification reception and CB update action
1516
- Upgrade dev dependency chai from ~1.8.0 to ~4.1.2
1617
- Upgrade dev dependency sinon from ~1.7.3 to ~6.1.0
17-
- Upgrade dev dependency sinon-chai from 2.4.0 to ~3.2.0
18+
- Upgrade dev dependency sinon-chai from 2.4.0 to ~3.2.0

documentation/architecture.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ A simplified format in JSON can be used to represent rules. The former visual ru
191191
```json
192192
{
193193
"name" : "prueba-test",
194-
"text" : "select *,\"prueba-test\" as ruleName from pattern [every ev=iotEvent((cast(id?, String) regexp \"asd\"))]",
194+
"text" : "select * from pattern [every ev=iotEvent((cast(id?, String) regexp \"asd\"))]",
195195
"action" : {
196196
"type" : "email",
197197
"template" : "DCA message",

documentation/models.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Example:
4949
},
5050
"subservice" : "/",
5151
"service" : "unknownt",
52-
"text" : "select *,\"ReglaId\" as ruleName from pattern [every ev=iotEvent((cast(`id`?, String) regexp \"^value.*\"))]"
52+
"text" : "select * from pattern [every ev=iotEvent((cast(`id`?, String) regexp \"^value.*\"))]"
5353
}
5454

5555
```

documentation/plain_rules.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ The “anatomy” of a rule is as follows
77
```json
88
{
99
"name":"blood_rule_update",
10-
"text":"select *,\"blood_rule_update\" as ruleName, *, ev.BloodPressure? as Pressure, ev.id? as Meter from pattern [every ev=iotEvent(cast(cast(BloodPressure?,String),float)>1.5 and type=\"BloodMeter\")]",
10+
"text":"select *, *, ev.BloodPressure? as Pressure, ev.id? as Meter from pattern [every ev=iotEvent(cast(cast(BloodPressure?,String),float)>1.5 and type=\"BloodMeter\")]",
1111
"action":{
1212
"type":"update",
1313
"parameters":{
@@ -36,16 +36,16 @@ EPL is documented in [Esper website](http://www.espertech.com/esper/esper-docume
3636
A EPL statement to use with perseo could be:
3737

3838
```
39-
select *, "blood_rule_update" as ruleName,
40-
ev.BloodPressure? as Pressure, ev.id? as Meter
39+
select *, ev.BloodPressure? as Pressure, ev.id? as Meter
4140
from pattern
42-
[every ev=iotEvent(cast(cast(BloodPressure?,String),float)>1.5 and type="BloodMeter")]
41+
[every ev=iotEvent(cast(cast(BloodPressure?,String),float)>1.5 and type="BloodMeter")]
4342
```
4443

45-
46-
* The rule name must be present with **ruleName** alias. It must be equal to the ‘name’ field of the rule object
4744
* The *from* pattern must name the event as **ev** and the event stream from which take events must be **iotEvent**
4845
* A *type=* condition must be concatenated for avoiding mixing different kinds of entities
46+
* The variable 'ruleName' in automatically added to the action, even if it is not present in the EPL text. The ruleName automatically added this way is retrieved as part of the EPL text when the rule is recovered using GET /rules or GET /rules/{name}.
47+
48+
**Backward compatibility note:** since version 1.8.0 it is not mandatory to specify the name of the rule as part of the EPL text. In fact, it is not recommendable to do that. However, for backward compatibility, it can be present as *ruleName* alias (`e.g: select *, "blood_rule_update" as ruleName...`) in the select clause. If present, it must be equal to the ‘name’ field of the rule object.
4949

5050
The used entity's attributes must be cast to `float` in case of being numeric (like in the example). Alphanumeric
5151
values must be cast to `String`. Nested cast to string and to float is something we are analyzing, and could be
@@ -527,7 +527,7 @@ could be used by a rule so
527527
```json
528528
{
529529
"name": "blood_rule_email_md",
530-
"text": "select *,\"blood_rule_email_md\" as ruleName, *,ev.BloodPressure? as Pression, ev.id? as Meter from pattern [every ev=iotEvent(cast(BloodPressure__metadata__crs__system?,String)=\"WGS84\" and type=\"BloodMeter\")]",
530+
"text": "select *, *,ev.BloodPressure? as Pression, ev.id? as Meter from pattern [every ev=iotEvent(cast(BloodPressure__metadata__crs__system?,String)=\"WGS84\" and type=\"BloodMeter\")]",
531531
"action": {
532532
"type": "email",
533533
"template": "Meter ${Meter} has pression ${Pression} (GEN RULE) and system is ${BloodPressure__metadata__crs__system}",
@@ -666,7 +666,7 @@ An example of rule taking advantage of these derived attributes could be:
666666
```json
667667
{
668668
"name": "rule_distance",
669-
"text": "select *, \"rule_distance\" as ruleName from pattern [every ev=iotEvent(Math.pow((cast(cast(position__x?,String),float) - 618618.8286057833), 2) + Math.pow((cast(cast(position__y?,String),float) - 9764160.736945232), 2) < Math.pow(5e3,2))]",
669+
"text": "select *, from pattern [every ev=iotEvent(Math.pow((cast(cast(position__x?,String),float) - 618618.8286057833), 2) + Math.pow((cast(cast(position__y?,String),float) - 9764160.736945232), 2) < Math.pow(5e3,2))]",
670670
"action": {
671671
"type": "email",
672672
"template": "${id} (${type}) is at ${position__lat}, ${position__lon} (${position__x}, ${position__y})",
@@ -846,7 +846,7 @@ A rule that will check if the employee has been hired in the last half hour, cou
846846
```json
847847
{
848848
"name": "rule_time",
849-
"text": "select *, \"rule_time\" as ruleName from pattern [every ev=iotEvent(cast(cast(hire__ts?,String),float) > current_timestamp - 30*60*1000)]",
849+
"text": "select *, from pattern [every ev=iotEvent(cast(cast(hire__ts?,String),float) > current_timestamp - 30*60*1000)]",
850850
"action": {
851851
"type": "email",
852852
"template": "So glad with our new ${role}, ${id}!",

lib/models/rules.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
*
2020
* For those usages not covered by the GNU Affero General Public License
2121
* please contact with::[contacto@tid.es]
22+
*
23+
* Modified by: Carlos Blanco - Future Internet Consulting and Development Solutions (FICODES)
2224
*/
2325
'use strict';
2426

@@ -132,6 +134,24 @@ function putR2core(rules, callback) {
132134
myutils.requestHelperWOMetrics('put', {url: config.perseoCore.rulesURL, json: rulesAndContexts}, callback);
133135
}
134136

137+
/**
138+
* Add ruleName if necessary in rule text field
139+
*
140+
* @param rule The rule object
141+
*/
142+
function normalizeRuleName(rule) {
143+
144+
var newAs = '"' + rule.name + '" as ruleName';
145+
// Add "name as ruleName" if not exist
146+
if (rule.text && rule.text.indexOf(' as ruleName') === -1) {
147+
var offset = 'select'.length + 1;
148+
var idx = rule.text.toLowerCase().indexOf('select') + offset;
149+
rule.text = rule.text = rule.text.slice(0, idx) + newAs + ', ' + rule.text.slice(idx);
150+
}
151+
152+
return rule;
153+
}
154+
135155
module.exports = {
136156
FindAll: function(service, subservice, callback) {
137157
rulesStore.FindAll(service, subservice, function(err, data) {
@@ -150,6 +170,10 @@ module.exports = {
150170
myutils.logErrorIf(localError);
151171
return callback(localError);
152172
}
173+
174+
// Normalize the rule text
175+
rule = normalizeRuleName(rule);
176+
153177
async.series(
154178
[
155179
function(localCallback) {
@@ -208,6 +232,10 @@ module.exports = {
208232
myutils.logErrorIf(localError);
209233
return callback(localError);
210234
}
235+
236+
// Normalize the rule text
237+
rule = normalizeRuleName(rule);
238+
211239
async.series(
212240
[
213241
delR2core.bind(null, rule),

test/component/rules_test.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
*
2020
* For those usages not covered by the GNU Affero General Public License
2121
* please contact with iot_support at tid dot es
22+
*
23+
* Modified by: Carlos Blanco - Future Internet Consulting and Development Solutions (FICODES)
2224
*/
2325

2426
'use strict';
@@ -87,6 +89,85 @@ describe('Rules', function() {
8789
}
8890
], done);
8991
});
92+
it('should add ruleName automatically when the rule text does not include "rule as ruleName"', function(done) {
93+
var rule = utilsT.loadExample('./test/data/good_rules/no_ruleName_in_text_rule_post.json');
94+
async.series([
95+
function(callback) {
96+
clients.PostRule(rule, function(error, data) {
97+
should.not.exist(error);
98+
data.should.have.property('statusCode', 200);
99+
return callback(null);
100+
});
101+
},
102+
function(callback) {
103+
clients.GetRule(rule.name, function(error, data) {
104+
should.not.exist(error);
105+
data.should.have.property('statusCode', 200);
106+
data.should.have.property('body');
107+
data.body.should.have.property('data');
108+
data.body.data.should.have.property('text',
109+
'select "x_post_auto" as ruleName, *, ev.xPressure? as Pression, ev.id? as Meter from ' +
110+
'pattern [every ev=iotEvent(cast(cast(xPressure?,String),float)>1.5 and type="xMeter")]');
111+
data.body.data.should.have.property('name', rule.name);
112+
113+
return callback();
114+
});
115+
}
116+
], done);
117+
});
118+
it('should preserve user ruleName', function(done) {
119+
var rule = utilsT.loadExample('./test/data/good_rules/blood_rule_post.json');
120+
async.series([
121+
function(callback) {
122+
clients.PostRule(rule, function(error, data) {
123+
should.not.exist(error);
124+
data.should.have.property('statusCode', 200);
125+
return callback(null);
126+
});
127+
},
128+
function(callback) {
129+
clients.GetRule(rule.name, function(error, data) {
130+
should.not.exist(error);
131+
data.should.have.property('statusCode', 200);
132+
data.should.have.property('body');
133+
data.body.should.have.property('data');
134+
data.body.data.should.have.property('text',
135+
'select *,\"blood_post\" as ruleName,ev.BloodPressure? as Pression, ev.id? as Meter from' +
136+
' pattern [every ev=iotEvent(cast(cast(BloodPressure?,String),float)>1.5 and ' +
137+
'type=\"BloodMeter\")]');
138+
data.body.data.should.have.property('name', rule.name);
139+
140+
return callback();
141+
});
142+
}
143+
], done);
144+
});
145+
it('should preserve user incorrect ruleName', function(done) {
146+
var rule = utilsT.loadExample('./test/data/bad_rules/rule_bad_ruleName_text_rule_post.json');
147+
async.series([
148+
function(callback) {
149+
clients.PostRule(rule, function(error, data) {
150+
should.not.exist(error);
151+
data.should.have.property('statusCode', 200);
152+
return callback(null);
153+
});
154+
},
155+
function(callback) {
156+
clients.GetRule(rule.name, function(error, data) {
157+
should.not.exist(error);
158+
data.should.have.property('statusCode', 200);
159+
data.should.have.property('body');
160+
data.body.should.have.property('data');
161+
data.body.data.should.have.property('text',
162+
'select *, "badRuleleName" as ruleName, ev.xPressure? as Pression, ev.id? as Meter from' +
163+
' pattern [every ev=iotEvent(cast(cast(xPressure?,String),float)>1.5 and type="xMeter")]');
164+
data.body.data.should.have.property('name', rule.name);
165+
166+
return callback();
167+
});
168+
}
169+
], done);
170+
});
90171
it('should return an error if core endpoint is not working', function(done) {
91172
var cases = utilsT.loadDirExamples('./test/data/good_rules');
92173
utilsT.setServerCode(400);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "theCorrectName",
3+
"text": "select *, \"badRuleleName\" as ruleName, ev.xPressure? as Pression, ev.id? as Meter from pattern [every ev=iotEvent(cast(cast(xPressure?,String),float)>1.5 and type=\"xMeter\")]",
4+
"action": {
5+
"type": "post",
6+
"template": "Meter ${Meter} has pression ${Pression}.",
7+
"parameters": {
8+
"url": "localhost:1111"
9+
}
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "x_post_auto",
3+
"text": "select *, ev.xPressure? as Pression, ev.id? as Meter from pattern [every ev=iotEvent(cast(cast(xPressure?,String),float)>1.5 and type=\"xMeter\")]",
4+
"action": {
5+
"type": "post",
6+
"template": "Meter ${Meter} has pression ${Pression}.",
7+
"parameters": {
8+
"url": "localhost:1111"
9+
}
10+
}
11+
}

0 commit comments

Comments
 (0)