Skip to content
Permalink
Browse files

Added option for continue on error for Rules

  • Loading branch information...
Miggets7 committed Aug 7, 2019
1 parent 6a6e899 commit a6e48b12da6656dd83163f0dca6d78febb0c16ae
Showing with 211 additions and 93 deletions.
  1. +1 −1 client/src/main/java/org/openremote/app/client/rules/asset/AssetRulesEditorActivity.java
  2. +1 −1 client/src/main/java/org/openremote/app/client/rules/tenant/TenantRulesEditorActivity.java
  3. +6 −4 manager/src/main/java/org/openremote/manager/rules/RulesEngine.java
  4. +3 −3 manager/src/main/java/org/openremote/manager/rules/RulesetStorageService.java
  5. +2 −2 manager/src/main/java/org/openremote/manager/setup/builtin/ManagerDemoSetup.java
  6. +7 −7 manager/src/main/java/org/openremote/manager/setup/builtin/RulesDemoSetup.java
  7. +15 −0 .../main/resources/org/openremote/manager/setup/database/V20190806_06__AddRulesetContinueOnError.sql
  8. +10 −7 model/src/main/java/org/openremote/model/rules/AssetRuleset.java
  9. +4 −4 model/src/main/java/org/openremote/model/rules/GlobalRuleset.java
  10. +18 −5 model/src/main/java/org/openremote/model/rules/Ruleset.java
  11. +6 −6 model/src/main/java/org/openremote/model/rules/TenantRuleset.java
  12. +1 −1 test/src/test/groovy/org/openremote/test/assets/AssetQueryTest.groovy
  13. +6 −3 test/src/test/groovy/org/openremote/test/console/ConsoleTest.groovy
  14. +12 −4 test/src/test/groovy/org/openremote/test/failure/RulesExecutionFailureTest.groovy
  15. +25 −4 test/src/test/groovy/org/openremote/test/rules/BasicRulesDeploymentTest.groovy
  16. +18 −8 test/src/test/groovy/org/openremote/test/rules/BasicRulesImport.groovy
  17. +6 −3 test/src/test/groovy/org/openremote/test/rules/BasicRulesProcessingTest.groovy
  18. +17 −17 test/src/test/groovy/org/openremote/test/rules/BasicRulesetResourceTest.groovy
  19. +3 −2 test/src/test/groovy/org/openremote/test/rules/residence/JsonRulesTest.groovy
  20. +3 −1 test/src/test/groovy/org/openremote/test/rules/residence/ResidenceAllLightsOffTest.groovy
  21. +3 −1 test/src/test/groovy/org/openremote/test/rules/residence/ResidenceAutoVentilationTest.groovy
  22. +3 −1 test/src/test/groovy/org/openremote/test/rules/residence/ResidenceNotifyAlarmTriggerTest.groovy
  23. +6 −2 test/src/test/groovy/org/openremote/test/rules/residence/ResidencePresenceDetectionTest.groovy
  24. +15 −5 test/src/test/groovy/org/openremote/test/rules/residence/ResidenceSmartSwitchTest.groovy
  25. +3 −1 test/src/test/groovy/org/openremote/test/rules/residence/ResidenceVacationModeTest.groovy
  26. +17 −0 test/src/test/resources/org/openremote/test/rules/BasicLoopingErrorRules.groovy
@@ -84,7 +84,7 @@ public void start(AcceptsView container, EventBus eventBus, Collection<EventRegi

@Override
protected AssetRuleset newRuleset() {
return new AssetRuleset(null, null, null, assetId, false);
return new AssetRuleset(null, null, null, assetId, false, false);
}

@Override
@@ -84,7 +84,7 @@ public void start(AcceptsView container, EventBus eventBus, Collection<EventRegi

@Override
protected TenantRuleset newRuleset() {
return new TenantRuleset(null, null, null, realm, false);
return new TenantRuleset(null, null, null, realm, false, false);
}

@Override
@@ -32,8 +32,8 @@
import org.openremote.manager.rules.facade.UsersFacade;
import org.openremote.manager.security.ManagerIdentityService;
import org.openremote.model.asset.Asset;
import org.openremote.model.attribute.MetaItemType;
import org.openremote.model.attribute.AttributeType;
import org.openremote.model.attribute.MetaItemType;
import org.openremote.model.query.filter.GeofencePredicate;
import org.openremote.model.query.filter.LocationAttributePredicate;
import org.openremote.model.rules.*;
@@ -434,10 +434,12 @@ protected void fireAllDeployments() {

// TODO We always stop on any error, good idea?
// TODO We only get here on LHS runtime errors, RHS runtime errors are in RuleFacts.onFailure()
stop();

// TODO We skip any other deployment when we hit the first error, good idea?
break;
if (!deployment.ruleset.isContinueOnError()) {
stop();
// TODO We skip any other deployment when we hit the first error, good idea?
break;
}
} finally {
// Reset facts after this firing (loop detection etc.)
facts.reset();
@@ -73,7 +73,7 @@ public void stop(Container container) throws Exception {
public List<GlobalRuleset> findGlobalRulesets(boolean onlyEnabled, Ruleset.Lang language, boolean fullyPopulate) {

String query = "select new org.openremote.model.rules.GlobalRuleset(" +
"rs.id, rs.version, rs.createdOn, rs.lastModified, rs.enabled, rs.name, rs.lang, rs.meta";
"rs.id, rs.version, rs.createdOn, rs.lastModified, rs.enabled, rs.name, rs.lang, rs.meta, rs.continueOnError";

query += fullyPopulate ? ", rs.rules" : ", cast(null as string)";

@@ -115,7 +115,7 @@ public void stop(Container container) throws Exception {

query += fullyPopulate ? ", rs.rules" : ", cast(null as string)";

query += ", rs.realm, rs.accessPublicRead) " +
query += ", rs.realm, rs.accessPublicRead, rs.continueOnError) " +
"from TenantRuleset rs " +
"where 1=1 ";

@@ -170,7 +170,7 @@ public void stop(Container container) throws Exception {

query += fullyPopulate ? ", rs.rules" : ", cast(null as string)";

query += ", a.realm, rs.assetId, rs.accessPublicRead) " +
query += ", a.realm, rs.assetId, rs.accessPublicRead, rs.continueOnError) " +
"from AssetRuleset rs, Asset a " +
"where rs.assetId = a.id ";

@@ -26,7 +26,6 @@
import org.openremote.manager.setup.AbstractManagerSetup;
import org.openremote.model.asset.Asset;
import org.openremote.model.asset.AssetAttribute;
import org.openremote.model.attribute.MetaItemType;
import org.openremote.model.asset.UserAsset;
import org.openremote.model.attribute.*;
import org.openremote.model.geo.GeoJSONPoint;
@@ -44,10 +43,10 @@
import java.util.Arrays;
import java.util.List;

import static org.openremote.model.attribute.MetaItemType.*;
import static org.openremote.model.asset.AssetType.*;
import static org.openremote.model.asset.agent.ProtocolConfiguration.initProtocolConfiguration;
import static org.openremote.model.attribute.AttributeValueType.*;
import static org.openremote.model.attribute.MetaItemType.*;
import static org.openremote.model.rules.Ruleset.Lang.GROOVY;

public class ManagerDemoSetup extends AbstractManagerSetup {
@@ -875,6 +874,7 @@ public void onStart() throws Exception {
AssetRuleset camera3Rules = new AssetRuleset(
"Camera3_Rules",
GROOVY, IOUtils.toString(getClass().getResource("/demo/rules/DemoSmartCityCamera.groovy"), "UTF-8"), camera3Asset.getId(),
false,
false
);
camera3Rules = rulesetStorageService.merge(camera3Rules);
@@ -55,35 +55,35 @@ public void onStart() throws Exception {
try (InputStream inputStream = RulesDemoSetup.class.getResourceAsStream("/demo/rules/DemoResidencePresenceDetection.groovy")) {
String rules = IOUtils.toString(inputStream, Charset.forName("utf-8"));
Ruleset ruleset = new AssetRuleset(
"Demo Residence - Presence Detection with motion and CO2 sensors", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false
"Demo Residence - Presence Detection with motion and CO2 sensors", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false, false
);
apartmentActionsRulesetId = rulesetStorageService.merge(ruleset).getId();
}
try (InputStream inputStream = RulesDemoSetup.class.getResourceAsStream("/demo/rules/DemoResidenceVacationMode.groovy")) {
String rules = IOUtils.toString(inputStream, Charset.forName("utf-8"));
Ruleset ruleset = new AssetRuleset(
"Demo Residence - Vacation Mode", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false
"Demo Residence - Vacation Mode", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false, false
);
apartmentActionsRulesetId = rulesetStorageService.merge(ruleset).getId();
}
try (InputStream inputStream = RulesDemoSetup.class.getResourceAsStream("/demo/rules/DemoResidenceAutoVentilation.groovy")) {
String rules = IOUtils.toString(inputStream, Charset.forName("utf-8"));
Ruleset ruleset = new AssetRuleset(
"Demo Residence - Auto Ventilation", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false
"Demo Residence - Auto Ventilation", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false, false
);
apartmentActionsRulesetId = rulesetStorageService.merge(ruleset).getId();
}
try (InputStream inputStream = RulesDemoSetup.class.getResourceAsStream("/demo/rules/DemoResidenceNotifyAlarmTrigger.groovy")) {
String rules = IOUtils.toString(inputStream, Charset.forName("utf-8"));
Ruleset ruleset = new AssetRuleset(
"Demo Residence - Notify Alarm Trigger", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false
"Demo Residence - Notify Alarm Trigger", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false, false
);
apartmentActionsRulesetId = rulesetStorageService.merge(ruleset).getId();
}
try (InputStream inputStream = RulesDemoSetup.class.getResourceAsStream("/demo/rules/DemoResidenceSmartSwitch.groovy")) {
String rules = IOUtils.toString(inputStream, Charset.forName("utf-8"));
Ruleset ruleset = new AssetRuleset(
"Demo Residence - Smart Start Switch", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false
"Demo Residence - Smart Start Switch", Ruleset.Lang.GROOVY, rules, managerDemoSetup.apartment1Id, false, false
);
apartmentActionsRulesetId = rulesetStorageService.merge(ruleset).getId();
}
@@ -92,15 +92,15 @@ public void onStart() throws Exception {
try (InputStream inputStream = RulesDemoSetup.class.getResourceAsStream("/demo/rules/DemoResidenceAllLightsOff.js")) {
String rules = IOUtils.toString(inputStream, Charset.forName("utf-8"));
Ruleset ruleset = new AssetRuleset(
"Demo Residence - All Lights Off", Ruleset.Lang.JAVASCRIPT, rules, managerDemoSetup.apartment2Id, false
"Demo Residence - All Lights Off", Ruleset.Lang.JAVASCRIPT, rules, managerDemoSetup.apartment2Id, false, false
);
apartmentActionsRulesetId = rulesetStorageService.merge(ruleset).getId();
}

try (InputStream inputStream = RulesDemoSetup.class.getResourceAsStream("/demo/rules/DemoConsoleLocation.groovy")) {
String rules = IOUtils.toString(inputStream, Charset.forName("utf-8"));
Ruleset ruleset = new TenantRuleset(
"Demo Console Location", Ruleset.Lang.GROOVY, rules, keycloakDemoSetup.tenantA.getRealm(), true
"Demo Console Location", Ruleset.Lang.GROOVY, rules, keycloakDemoSetup.tenantA.getRealm(), true, false
);
tenantARulesetId = rulesetStorageService.merge(ruleset).getId();
}
@@ -0,0 +1,15 @@
/*
Adds continueOnError column to rulesets
*/

-- UPDATE GLOBAL_RULESET
ALTER TABLE GLOBAL_RULESET
ADD COLUMN CONTINUE_ON_ERROR BOOLEAN NOT NULL DEFAULT false;

-- UPDATE ASSET_RULESET
ALTER TABLE ASSET_RULESET
ADD COLUMN CONTINUE_ON_ERROR BOOLEAN NOT NULL DEFAULT false;

-- UPDATE TENANT_RULESET
ALTER TABLE TENANT_RULESET
ADD COLUMN CONTINUE_ON_ERROR BOOLEAN NOT NULL DEFAULT false;
@@ -21,7 +21,10 @@

import org.openremote.model.value.ObjectValue;

import javax.persistence.*;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.util.Date;

/**
@@ -46,20 +49,20 @@
public AssetRuleset() {
}

public AssetRuleset(String name, Lang lang, String rules, String assetId, boolean accessPublicRead) {
super(name, rules, lang);
public AssetRuleset(String name, Lang lang, String rules, String assetId, boolean accessPublicRead, boolean continueOnError) {
super(name, rules, lang, continueOnError);
this.assetId = assetId;
this.accessPublicRead = accessPublicRead;
}

public AssetRuleset(String name, Lang lang, ObjectValue meta, String rules, String assetId, boolean accessPublicRead) {
super(name, rules, lang, meta);
public AssetRuleset(String name, Lang lang, ObjectValue meta, String rules, String assetId, boolean accessPublicRead, boolean continueOnError) {
super(name, rules, lang, meta, continueOnError);
this.assetId = assetId;
this.accessPublicRead = accessPublicRead;
}

public AssetRuleset(long id, long version, Date createdOn, Date lastModified, boolean enabled, String name, Lang lang, ObjectValue meta, String rules, String realm, String assetId, boolean accessPublicRead) {
super(id, version, createdOn, lastModified, name, enabled, rules, lang, meta);
public AssetRuleset(long id, long version, Date createdOn, Date lastModified, boolean enabled, String name, Lang lang, ObjectValue meta, String rules, String realm, String assetId, boolean accessPublicRead, boolean continueOnError) {
super(id, version, createdOn, lastModified, name, enabled, rules, lang, meta, continueOnError);
this.assetId = assetId;
this.realm = realm;
this.accessPublicRead = accessPublicRead;
@@ -37,16 +37,16 @@
public GlobalRuleset() {
}

public GlobalRuleset(long id, long version, Date createdOn, Date lastModified, boolean enabled, String name, Lang lang, ObjectValue meta, String rules) {
super(id, version, createdOn, lastModified, name, enabled, rules, lang, meta);
public GlobalRuleset(long id, long version, Date createdOn, Date lastModified, boolean enabled, String name, Lang lang, ObjectValue meta, boolean continueOnError, String rules) {
super(id, version, createdOn, lastModified, name, enabled, rules, lang, meta, continueOnError);
}

public GlobalRuleset(String name, Lang lang, String rules) {
super(name, rules, lang);
super(name, rules, lang, false);
}

public GlobalRuleset(String name, Lang lang, String rules, ObjectValue meta) {
super(name, rules, lang, meta);
super(name, rules, lang, meta, false);
}

@Override
@@ -189,6 +189,9 @@ public String getEmptyRulesExample() {
@org.hibernate.annotations.Type(type = PERSISTENCE_JSON_OBJECT_TYPE)
protected ObjectValue meta;

@Column(name = "CONTINUE_ON_ERROR", nullable = false)
protected boolean continueOnError = false;

@Transient
protected RulesetStatus status;

@@ -199,10 +202,10 @@ protected Ruleset() {
}

public Ruleset(long id, long version, Date createdOn, Date lastModified, String name, boolean enabled, Lang lang) {
this(id, version, createdOn, lastModified, name, enabled, null, lang, null);
this(id, version, createdOn, lastModified, name, enabled, null, lang, null, false);
}

public Ruleset(long id, long version, Date createdOn, Date lastModified, String name, boolean enabled, String rules, Lang lang, ObjectValue meta) {
public Ruleset(long id, long version, Date createdOn, Date lastModified, String name, boolean enabled, String rules, Lang lang, ObjectValue meta, boolean continueOnError) {
this.id = id;
this.version = version;
this.createdOn = createdOn;
@@ -212,17 +215,19 @@ public Ruleset(long id, long version, Date createdOn, Date lastModified, String
this.rules = rules;
this.lang = lang;
this.meta = meta;
this.continueOnError = continueOnError;
}

public Ruleset(String name, String rules, Lang lang, ObjectValue meta) {
this(name, rules, lang);
public Ruleset(String name, String rules, Lang lang, ObjectValue meta, boolean continueOnError) {
this(name, rules, lang, continueOnError);
this.meta = meta;
}

public Ruleset(String name, String rules, Lang lang) {
public Ruleset(String name, String rules, Lang lang, boolean continueOnError) {
this.name = name;
this.rules = rules;
this.lang = lang;
this.continueOnError = continueOnError;
}

public Long getId() {
@@ -310,4 +315,12 @@ public String getError() {
public void setError(String error) {
this.error = error;
}

public boolean isContinueOnError() {
return continueOnError;
}

public void setContinueOnError(boolean continueOnError) {
this.continueOnError = continueOnError;
}
}
@@ -45,20 +45,20 @@
public TenantRuleset() {
}

public TenantRuleset(String name, Lang lang, String rules, String realm, boolean accessPublicRead) {
super(name, rules, lang);
public TenantRuleset(String name, Lang lang, String rules, String realm, boolean accessPublicRead, boolean continueOnError) {
super(name, rules, lang, continueOnError);
this.realm = realm;
this.accessPublicRead = accessPublicRead;
}

public TenantRuleset(String name, Lang lang, ObjectValue meta, String rules, String realm, boolean accessPublicRead) {
super(name, rules, lang, meta);
public TenantRuleset(String name, Lang lang, ObjectValue meta, String rules, String realm, boolean accessPublicRead, boolean continueOnError) {
super(name, rules, lang, meta, continueOnError);
this.realm = realm;
this.accessPublicRead = accessPublicRead;
}

public TenantRuleset(long id, long version, Date createdOn, Date lastModified, boolean enabled, String name, Lang lang, ObjectValue meta, String rules, String realm, boolean accessPublicRead) {
super(id, version, createdOn, lastModified, name, enabled, rules, lang, meta);
public TenantRuleset(long id, long version, Date createdOn, Date lastModified, boolean enabled, String name, Lang lang, ObjectValue meta, String rules, String realm, boolean accessPublicRead, boolean continueOnError) {
super(id, version, createdOn, lastModified, name, enabled, rules, lang, meta, continueOnError);
this.realm = realm;
this.accessPublicRead = accessPublicRead;
}
@@ -861,7 +861,7 @@ class AssetQueryTest extends Specification implements ManagerContainerTrait {
def lobby = assetStorageService.find(managerDemoSetup.lobbyId, true)

lobby.addAttributes(
new AssetAttribute("openingDate", DATETIME, Values.create("2018-01-28T15:00:00"))
new AssetAttribute("openingDate", DATETIME, Values.create("2018-01-28T15:00:00+00:00"))
)
lobby = assetStorageService.merge(lobby)

@@ -98,8 +98,9 @@ class ConsoleTest extends Specification implements ManagerContainerTrait {
and: "the demo location predicate console rules are loaded"
Ruleset ruleset = new TenantRuleset(
"Demo Tenant A - Console Location", Ruleset.Lang.GROOVY, getClass().getResource("/demo/rules/DemoConsoleLocation.groovy").text,
keycloakDemoSetup.tenantA.realm
, false
keycloakDemoSetup.tenantA.realm,
false,
false
)
rulesetStorageService.merge(ruleset)

@@ -500,7 +501,9 @@ class ConsoleTest extends Specification implements ManagerContainerTrait {
when: "a new ruleset is deployed on the console parent asset with multiple location predicate rules (including a duplicate and a rectangular predicate)"
def newRuleset = new AssetRuleset(
"Console test location predicates", Ruleset.Lang.GROOVY, getClass().getResource("/org/openremote/test/rules/BasicLocationPredicates.groovy").text,
testUser3Console1.parentId, false
testUser3Console1.parentId,
false,
false
)
newRuleset = rulesetStorageService.merge(newRuleset)
RulesEngine consoleParentEngine = null
@@ -30,7 +30,9 @@ class RulesExecutionFailureTest extends Specification implements ManagerContaine
and: "some rules"
Ruleset ruleset = new AssetRuleset(
"Failure Ruleset", Ruleset.Lang.GROOVY, getClass().getResource("/org/openremote/test/failure/RulesFailureConditionInvalidReturn.groovy").text,
managerDemoSetup.apartment2Id, false
managerDemoSetup.apartment2Id,
false,
false
)
ruleset = rulesetStorageService.merge(ruleset)

@@ -64,7 +66,9 @@ class RulesExecutionFailureTest extends Specification implements ManagerContaine
and: "some rules"
Ruleset ruleset = new AssetRuleset(
"Failure Ruleset", Ruleset.Lang.GROOVY, getClass().getResource("/org/openremote/test/failure/RulesFailureConditionThrowsException.groovy").text,
managerDemoSetup.apartment2Id, false
managerDemoSetup.apartment2Id,
false,
false
)
ruleset = rulesetStorageService.merge(ruleset)

@@ -98,7 +102,9 @@ class RulesExecutionFailureTest extends Specification implements ManagerContaine
and: "some rules"
Ruleset ruleset = new AssetRuleset(
"Failure Ruleset", Ruleset.Lang.GROOVY, getClass().getResource("/org/openremote/test/failure/RulesFailureActionThrowsException.groovy").text,
managerDemoSetup.apartment2Id, false
managerDemoSetup.apartment2Id,
false,
false
)
ruleset = rulesetStorageService.merge(ruleset)

@@ -132,7 +138,9 @@ class RulesExecutionFailureTest extends Specification implements ManagerContaine
and: "some rules"
Ruleset ruleset = new AssetRuleset(
"Failure Ruleset", Ruleset.Lang.GROOVY, getClass().getResource("/org/openremote/test/failure/RulesFailureLoop.groovy").text,
managerDemoSetup.apartment2Id, false
managerDemoSetup.apartment2Id,
false,
false
)
ruleset = rulesetStorageService.merge(ruleset)

1 comment on commit a6e48b1

@richturner

This comment has been minimized.

Copy link
Member

commented on a6e48b1 Aug 7, 2019

Hi Michael,

Looks good, please make sure that if a loop is detected the rules still stop as that is one error we definitely should not continue on.

Please sign in to comment.
You can’t perform that action at this time.