Merge pull request #13738 from dskarzh/improve-script-errors
Improve rule engine and calculated fields test script errors
This commit is contained in:
commit
aed4af9477
@ -17,19 +17,21 @@ package org.thingsboard.server.controller;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
@ -41,7 +43,6 @@ import org.thingsboard.script.api.tbel.TbelCfTsRollingArg;
|
||||
import org.thingsboard.script.api.tbel.TbelInvokeService;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.EventInfo;
|
||||
import org.thingsboard.server.common.data.HasTenantId;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedField;
|
||||
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
|
||||
import org.thingsboard.server.common.data.event.EventType;
|
||||
@ -49,17 +50,14 @@ import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.common.data.id.CalculatedFieldId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||
import org.thingsboard.server.common.data.id.HasId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.config.annotations.ApiOperation;
|
||||
import org.thingsboard.server.dao.event.EventService;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldScriptEngine;
|
||||
import org.thingsboard.server.service.cf.ctx.state.CalculatedFieldTbelScriptEngine;
|
||||
import org.thingsboard.server.service.entitiy.cf.TbCalculatedFieldService;
|
||||
import org.thingsboard.server.service.security.model.SecurityUser;
|
||||
import org.thingsboard.server.service.security.permission.Operation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -100,30 +98,30 @@ public class CalculatedFieldController extends BaseController {
|
||||
|
||||
private static final String TEST_SCRIPT_EXPRESSION =
|
||||
"Execute the Script expression and return the result. The format of request: \n\n"
|
||||
+ MARKDOWN_CODE_BLOCK_START
|
||||
+ "{\n" +
|
||||
" \"expression\": \"var temp = 0; foreach(element: temperature.values) {temp += element.value;} var avgTemperature = temp / temperature.values.size(); var adjustedTemperature = avgTemperature + 0.1 * humidity.value; return {\\\"adjustedTemperature\\\": adjustedTemperature};\",\n" +
|
||||
" \"arguments\": {\n" +
|
||||
" \"temperature\": {\n" +
|
||||
" \"type\": \"TS_ROLLING\",\n" +
|
||||
" \"timeWindow\": {\n" +
|
||||
" \"startTs\": 1739775630002,\n" +
|
||||
" \"endTs\": 65432211,\n" +
|
||||
" \"limit\": 5\n" +
|
||||
" },\n" +
|
||||
" \"values\": [\n" +
|
||||
" { \"ts\": 1739775639851, \"value\": 23 },\n" +
|
||||
" { \"ts\": 1739775664561, \"value\": 43 },\n" +
|
||||
" { \"ts\": 1739775713079, \"value\": 15 },\n" +
|
||||
" { \"ts\": 1739775999522, \"value\": 34 },\n" +
|
||||
" { \"ts\": 1739776228452, \"value\": 22 }\n" +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"humidity\": { \"type\": \"SINGLE_VALUE\", \"ts\": 1739776478057, \"value\": 23 }\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
+ MARKDOWN_CODE_BLOCK_END
|
||||
+ "\n\n Expected result JSON contains \"output\" and \"error\".";
|
||||
+ MARKDOWN_CODE_BLOCK_START
|
||||
+ "{\n" +
|
||||
" \"expression\": \"var temp = 0; foreach(element: temperature.values) {temp += element.value;} var avgTemperature = temp / temperature.values.size(); var adjustedTemperature = avgTemperature + 0.1 * humidity.value; return {\\\"adjustedTemperature\\\": adjustedTemperature};\",\n" +
|
||||
" \"arguments\": {\n" +
|
||||
" \"temperature\": {\n" +
|
||||
" \"type\": \"TS_ROLLING\",\n" +
|
||||
" \"timeWindow\": {\n" +
|
||||
" \"startTs\": 1739775630002,\n" +
|
||||
" \"endTs\": 65432211,\n" +
|
||||
" \"limit\": 5\n" +
|
||||
" },\n" +
|
||||
" \"values\": [\n" +
|
||||
" { \"ts\": 1739775639851, \"value\": 23 },\n" +
|
||||
" { \"ts\": 1739775664561, \"value\": 43 },\n" +
|
||||
" { \"ts\": 1739775713079, \"value\": 15 },\n" +
|
||||
" { \"ts\": 1739775999522, \"value\": 34 },\n" +
|
||||
" { \"ts\": 1739776228452, \"value\": 22 }\n" +
|
||||
" ]\n" +
|
||||
" },\n" +
|
||||
" \"humidity\": { \"type\": \"SINGLE_VALUE\", \"ts\": 1739776478057, \"value\": 23 }\n" +
|
||||
" }\n" +
|
||||
"}"
|
||||
+ MARKDOWN_CODE_BLOCK_END
|
||||
+ "\n\n Expected result JSON contains \"output\" and \"error\".";
|
||||
|
||||
@ApiOperation(value = "Create Or Update Calculated Field (saveCalculatedField)",
|
||||
notes = "Creates or Updates the Calculated Field. When creating calculated field, platform generates Calculated Field Id as " + UUID_WIKI_LINK +
|
||||
@ -133,13 +131,12 @@ public class CalculatedFieldController extends BaseController {
|
||||
"Remove 'id', 'tenantId' from the request body example (below) to create new Calculated Field entity. "
|
||||
+ TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/calculatedField", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PostMapping("/calculatedField")
|
||||
public CalculatedField saveCalculatedField(@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "A JSON value representing the calculated field.")
|
||||
@RequestBody CalculatedField calculatedField) throws Exception {
|
||||
calculatedField.setTenantId(getTenantId());
|
||||
checkEntityId(calculatedField.getEntityId(), Operation.WRITE_CALCULATED_FIELD);
|
||||
checkReferencedEntities(calculatedField.getConfiguration(), getCurrentUser());
|
||||
checkReferencedEntities(calculatedField.getConfiguration());
|
||||
return tbCalculatedFieldService.save(calculatedField, getCurrentUser());
|
||||
}
|
||||
|
||||
@ -147,8 +144,7 @@ public class CalculatedFieldController extends BaseController {
|
||||
notes = "Fetch the Calculated Field object based on the provided Calculated Field Id."
|
||||
)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/calculatedField/{calculatedFieldId}", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@GetMapping("/calculatedField/{calculatedFieldId}")
|
||||
public CalculatedField getCalculatedFieldById(@Parameter @PathVariable(CALCULATED_FIELD_ID) String strCalculatedFieldId) throws ThingsboardException {
|
||||
checkParameter(CALCULATED_FIELD_ID, strCalculatedFieldId);
|
||||
CalculatedFieldId calculatedFieldId = new CalculatedFieldId(toUUID(strCalculatedFieldId));
|
||||
@ -162,8 +158,7 @@ public class CalculatedFieldController extends BaseController {
|
||||
notes = "Fetch the Calculated Fields based on the provided Entity Id."
|
||||
)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/{entityType}/{entityId}/calculatedFields", params = {"pageSize", "page"}, method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/{entityType}/{entityId}/calculatedFields", params = {"pageSize", "page"})
|
||||
public PageData<CalculatedField> getCalculatedFieldsByEntityId(
|
||||
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
|
||||
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
|
||||
@ -182,8 +177,8 @@ public class CalculatedFieldController extends BaseController {
|
||||
@ApiOperation(value = "Delete Calculated Field (deleteCalculatedField)",
|
||||
notes = "Deletes the calculated field. Referencing non-existing Calculated Field Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/calculatedField/{calculatedFieldId}", method = RequestMethod.DELETE)
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
@DeleteMapping("/calculatedField/{calculatedFieldId}")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public void deleteCalculatedField(@PathVariable(CALCULATED_FIELD_ID) String strCalculatedFieldId) throws Exception {
|
||||
checkParameter(CALCULATED_FIELD_ID, strCalculatedFieldId);
|
||||
CalculatedFieldId calculatedFieldId = new CalculatedFieldId(toUUID(strCalculatedFieldId));
|
||||
@ -196,8 +191,7 @@ public class CalculatedFieldController extends BaseController {
|
||||
notes = "Gets latest calculated field debug event for specified calculated field id. " +
|
||||
"Referencing non-existing calculated field id will cause an error. " + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/calculatedField/{calculatedFieldId}/debug", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@GetMapping("/calculatedField/{calculatedFieldId}/debug")
|
||||
public JsonNode getLatestCalculatedFieldDebugEvent(@Parameter @PathVariable(CALCULATED_FIELD_ID) String strCalculatedFieldId) throws ThingsboardException {
|
||||
checkParameter(CALCULATED_FIELD_ID, strCalculatedFieldId);
|
||||
CalculatedFieldId calculatedFieldId = new CalculatedFieldId(toUUID(strCalculatedFieldId));
|
||||
@ -212,15 +206,13 @@ public class CalculatedFieldController extends BaseController {
|
||||
@ApiOperation(value = "Test Script expression",
|
||||
notes = TEST_SCRIPT_EXPRESSION + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/calculatedField/testScript", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PostMapping("/calculatedField/testScript")
|
||||
public JsonNode testScript(
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Test calculated field TBEL expression.")
|
||||
@RequestBody JsonNode inputParams) {
|
||||
String expression = inputParams.get("expression").asText();
|
||||
Map<String, TbelCfArg> arguments = Objects.requireNonNullElse(
|
||||
JacksonUtil.convertValue(inputParams.get("arguments"), new TypeReference<>() {
|
||||
}),
|
||||
JacksonUtil.convertValue(inputParams.get("arguments"), new TypeReference<>() {}),
|
||||
Collections.emptyMap()
|
||||
);
|
||||
|
||||
@ -231,12 +223,13 @@ public class CalculatedFieldController extends BaseController {
|
||||
String output = "";
|
||||
String errorText = "";
|
||||
|
||||
CalculatedFieldTbelScriptEngine engine = null;
|
||||
try {
|
||||
if (tbelInvokeService == null) {
|
||||
throw new IllegalArgumentException("TBEL script engine is disabled!");
|
||||
}
|
||||
|
||||
CalculatedFieldScriptEngine calculatedFieldScriptEngine = new CalculatedFieldTbelScriptEngine(
|
||||
engine = new CalculatedFieldTbelScriptEngine(
|
||||
getTenantId(),
|
||||
tbelInvokeService,
|
||||
expression,
|
||||
@ -254,17 +247,20 @@ public class CalculatedFieldController extends BaseController {
|
||||
}
|
||||
}
|
||||
|
||||
JsonNode json = calculatedFieldScriptEngine.executeJsonAsync(args).get(TIMEOUT, TimeUnit.SECONDS);
|
||||
JsonNode json = engine.executeJsonAsync(args).get(TIMEOUT, TimeUnit.SECONDS);
|
||||
output = JacksonUtil.toString(json);
|
||||
} catch (Exception e) {
|
||||
log.error("Error evaluating expression", e);
|
||||
errorText = e.getMessage();
|
||||
Throwable rootCause = ExceptionUtils.getRootCause(e);
|
||||
errorText = ObjectUtils.firstNonNull(rootCause.getMessage(), e.getMessage(), e.getClass().getSimpleName());
|
||||
} finally {
|
||||
if (engine != null) {
|
||||
engine.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
ObjectNode result = JacksonUtil.newObjectNode();
|
||||
result.put("output", output);
|
||||
result.put("error", errorText);
|
||||
return result;
|
||||
return JacksonUtil.newObjectNode()
|
||||
.put("output", output)
|
||||
.put("error", errorText);
|
||||
}
|
||||
|
||||
private long getLatestTimestamp(Map<String, TbelCfArg> arguments) {
|
||||
@ -281,7 +277,7 @@ public class CalculatedFieldController extends BaseController {
|
||||
return lastUpdateTimestamp == -1 ? System.currentTimeMillis() : lastUpdateTimestamp;
|
||||
}
|
||||
|
||||
private <E extends HasId<I> & HasTenantId, I extends EntityId> void checkReferencedEntities(CalculatedFieldConfiguration calculatedFieldConfig, SecurityUser user) throws ThingsboardException {
|
||||
private void checkReferencedEntities(CalculatedFieldConfiguration calculatedFieldConfig) throws ThingsboardException {
|
||||
List<EntityId> referencedEntityIds = calculatedFieldConfig.getReferencedEntities();
|
||||
for (EntityId referencedEntityId : referencedEntityIds) {
|
||||
EntityType entityType = referencedEntityId.getEntityType();
|
||||
@ -290,8 +286,7 @@ public class CalculatedFieldController extends BaseController {
|
||||
return;
|
||||
}
|
||||
case CUSTOMER, ASSET, DEVICE -> checkEntityId(referencedEntityId, Operation.READ);
|
||||
default ->
|
||||
throw new IllegalArgumentException("Calculated fields do not support '" + entityType + "' for referenced entities.");
|
||||
default -> throw new IllegalArgumentException("Calculated fields do not support '" + entityType + "' for referenced entities.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.thingsboard.server.controller;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
@ -23,16 +22,19 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
@ -155,9 +157,8 @@ public class RuleChainController extends BaseController {
|
||||
|
||||
@ApiOperation(value = "Get Rule Chain (getRuleChainById)",
|
||||
notes = "Fetch the Rule Chain object based on the provided Rule Chain Id. " + RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@GetMapping("/ruleChain/{ruleChainId}")
|
||||
public RuleChain getRuleChainById(
|
||||
@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
@ -169,9 +170,8 @@ public class RuleChainController extends BaseController {
|
||||
@ApiOperation(value = "Get Rule Chain output labels (getRuleChainOutputLabels)",
|
||||
notes = "Fetch the unique labels for the \"output\" Rule Nodes that belong to the Rule Chain based on the provided Rule Chain Id. "
|
||||
+ RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/{ruleChainId}/output/labels", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@GetMapping("/ruleChain/{ruleChainId}/output/labels")
|
||||
public Set<String> getRuleChainOutputLabels(
|
||||
@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
@ -184,9 +184,8 @@ public class RuleChainController extends BaseController {
|
||||
@ApiOperation(value = "Get output labels usage (getRuleChainOutputLabelsUsage)",
|
||||
notes = "Fetch the list of rule chains and the relation types (labels) they use to process output of the current rule chain based on the provided Rule Chain Id. "
|
||||
+ RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/{ruleChainId}/output/labels/usage", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@GetMapping("/ruleChain/{ruleChainId}/output/labels/usage")
|
||||
public List<RuleChainOutputLabelsUsage> getRuleChainOutputLabelsUsage(
|
||||
@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
@ -198,9 +197,8 @@ public class RuleChainController extends BaseController {
|
||||
|
||||
@ApiOperation(value = "Get Rule Chain (getRuleChainById)",
|
||||
notes = "Fetch the Rule Chain Metadata object based on the provided Rule Chain Id. " + RULE_CHAIN_METADATA_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/{ruleChainId}/metadata", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@GetMapping("/ruleChain/{ruleChainId}/metadata")
|
||||
public RuleChainMetaData getRuleChainMetaData(
|
||||
@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
@ -218,9 +216,8 @@ public class RuleChainController extends BaseController {
|
||||
"\n\n" + RULE_CHAIN_DESCRIPTION +
|
||||
"Remove 'id', 'tenantId' from the request body example (below) to create new Rule Chain entity." +
|
||||
TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@PostMapping("/ruleChain")
|
||||
public RuleChain saveRuleChain(
|
||||
@Parameter(description = "A JSON value representing the rule chain.")
|
||||
@RequestBody RuleChain ruleChain) throws Exception {
|
||||
@ -232,9 +229,8 @@ public class RuleChainController extends BaseController {
|
||||
@ApiOperation(value = "Create Default Rule Chain",
|
||||
notes = "Create rule chain from template, based on the specified name in the request. " +
|
||||
"Creates the rule chain based on the template that is used to create root rule chain. " + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/device/default", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@PostMapping("/ruleChain/device/default")
|
||||
public RuleChain saveRuleChain(
|
||||
@Parameter(description = "A JSON value representing the request.")
|
||||
@RequestBody DefaultRuleChainCreateRequest request) throws Exception {
|
||||
@ -245,9 +241,8 @@ public class RuleChainController extends BaseController {
|
||||
|
||||
@ApiOperation(value = "Set Root Rule Chain (setRootRuleChain)",
|
||||
notes = "Makes the rule chain to be root rule chain. Updates previous root rule chain as well. " + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/{ruleChainId}/root", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@PostMapping("/ruleChain/{ruleChainId}/root")
|
||||
public RuleChain setRootRuleChain(
|
||||
@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
@ -259,9 +254,8 @@ public class RuleChainController extends BaseController {
|
||||
|
||||
@ApiOperation(value = "Update Rule Chain Metadata",
|
||||
notes = "Updates the rule chain metadata. " + RULE_CHAIN_METADATA_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/metadata", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@PostMapping("/ruleChain/metadata")
|
||||
public RuleChainMetaData saveRuleChainMetaData(
|
||||
@Parameter(description = "A JSON value representing the rule chain metadata.")
|
||||
@RequestBody RuleChainMetaData ruleChainMetaData,
|
||||
@ -284,8 +278,7 @@ public class RuleChainController extends BaseController {
|
||||
@ApiOperation(value = "Get Rule Chains (getRuleChains)",
|
||||
notes = "Returns a page of Rule Chains owned by tenant. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/ruleChains", params = {"pageSize", "page"})
|
||||
public PageData<RuleChain> getRuleChains(
|
||||
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
|
||||
@RequestParam int pageSize,
|
||||
@ -302,7 +295,7 @@ public class RuleChainController extends BaseController {
|
||||
TenantId tenantId = getCurrentUser().getTenantId();
|
||||
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
|
||||
RuleChainType type = RuleChainType.CORE;
|
||||
if (typeStr != null && typeStr.trim().length() > 0) {
|
||||
if (StringUtils.isNotBlank(typeStr)) {
|
||||
type = RuleChainType.valueOf(typeStr);
|
||||
}
|
||||
return checkNotNull(ruleChainService.findTenantRuleChainsByType(tenantId, type, pageLink));
|
||||
@ -311,9 +304,9 @@ public class RuleChainController extends BaseController {
|
||||
@ApiOperation(value = "Delete rule chain (deleteRuleChain)",
|
||||
notes = "Deletes the rule chain. Referencing non-existing rule chain Id will cause an error. " +
|
||||
"Referencing rule chain that is used in the device profiles will cause an error." + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/{ruleChainId}", method = RequestMethod.DELETE)
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@DeleteMapping("/ruleChain/{ruleChainId}")
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public void deleteRuleChain(
|
||||
@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
@ -326,9 +319,8 @@ public class RuleChainController extends BaseController {
|
||||
@ApiOperation(value = "Get latest input message (getLatestRuleNodeDebugInput)",
|
||||
notes = "Gets the input message from the debug events for specified Rule Chain Id. " +
|
||||
"Referencing non-existing rule chain Id will cause an error. " + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleNode/{ruleNodeId}/debugIn", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@GetMapping("/ruleNode/{ruleNodeId}/debugIn")
|
||||
public JsonNode getLatestRuleNodeDebugInput(
|
||||
@Parameter(description = RULE_NODE_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_NODE_ID) String strRuleNodeId) throws ThingsboardException {
|
||||
@ -343,8 +335,7 @@ public class RuleChainController extends BaseController {
|
||||
@ApiOperation(value = "Is TBEL script executor enabled",
|
||||
notes = "Returns 'True' if the TBEL script execution is enabled" + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/tbelEnabled", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@GetMapping("/ruleChain/tbelEnabled")
|
||||
public Boolean isTbelEnabled() {
|
||||
return tbelEnabled;
|
||||
}
|
||||
@ -352,13 +343,12 @@ public class RuleChainController extends BaseController {
|
||||
@ApiOperation(value = "Test Script function",
|
||||
notes = TEST_SCRIPT_FUNCTION + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/testScript", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PostMapping("/ruleChain/testScript")
|
||||
public JsonNode testScript(
|
||||
@Parameter(description = "Script language: JS or TBEL")
|
||||
@RequestParam(required = false) ScriptLanguage scriptLang,
|
||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Test JS request. See API call description above.")
|
||||
@RequestBody JsonNode inputParams) throws ThingsboardException, JsonProcessingException {
|
||||
@RequestBody JsonNode inputParams) {
|
||||
String script = inputParams.get("script").asText();
|
||||
String scriptType = inputParams.get("scriptType").asText();
|
||||
JsonNode argNamesJson = inputParams.get("argNames");
|
||||
@ -366,8 +356,7 @@ public class RuleChainController extends BaseController {
|
||||
|
||||
String data = inputParams.get("msg").asText();
|
||||
JsonNode metadataJson = inputParams.get("metadata");
|
||||
Map<String, String> metadata = JacksonUtil.convertValue(metadataJson, new TypeReference<Map<String, String>>() {
|
||||
});
|
||||
Map<String, String> metadata = JacksonUtil.convertValue(metadataJson, new TypeReference<>() {});
|
||||
String msgType = inputParams.get("msgType").asText();
|
||||
String output = "";
|
||||
String errorText = "";
|
||||
@ -384,55 +373,40 @@ public class RuleChainController extends BaseController {
|
||||
}
|
||||
engine = new RuleNodeTbelScriptEngine(getTenantId(), tbelInvokeService, script, argNames);
|
||||
}
|
||||
TbMsg inMsg = TbMsg.newMsg()
|
||||
|
||||
var inMsg = TbMsg.newMsg()
|
||||
.type(msgType)
|
||||
.copyMetaData(new TbMsgMetaData(metadata))
|
||||
.dataType(TbMsgDataType.JSON)
|
||||
.data(data)
|
||||
.build();
|
||||
switch (scriptType) {
|
||||
case "update":
|
||||
output = msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
|
||||
break;
|
||||
case "generate":
|
||||
output = msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
|
||||
break;
|
||||
case "filter":
|
||||
boolean result = engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
|
||||
output = Boolean.toString(result);
|
||||
break;
|
||||
case "switch":
|
||||
Set<String> states = engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
|
||||
output = JacksonUtil.toString(states);
|
||||
break;
|
||||
case "json":
|
||||
JsonNode json = engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
|
||||
output = JacksonUtil.toString(json);
|
||||
break;
|
||||
case "string":
|
||||
output = engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported script type: " + scriptType);
|
||||
}
|
||||
|
||||
output = switch (scriptType) {
|
||||
case "update" -> msgToOutput(engine.executeUpdateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
|
||||
case "generate" -> msgToOutput(engine.executeGenerateAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
|
||||
case "filter" -> Boolean.toString(engine.executeFilterAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
|
||||
case "switch" -> JacksonUtil.toString(engine.executeSwitchAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
|
||||
case "json" -> JacksonUtil.toString(engine.executeJsonAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS));
|
||||
case "string" -> engine.executeToStringAsync(inMsg).get(TIMEOUT, TimeUnit.SECONDS);
|
||||
default -> throw new IllegalArgumentException("Unsupported script type: " + scriptType);
|
||||
};
|
||||
} catch (Exception e) {
|
||||
log.error("Error evaluating JS function", e);
|
||||
errorText = e.getMessage();
|
||||
Throwable rootCause = ExceptionUtils.getRootCause(e);
|
||||
errorText = ObjectUtils.firstNonNull(rootCause.getMessage(), e.getMessage(), e.getClass().getSimpleName());
|
||||
} finally {
|
||||
if (engine != null) {
|
||||
engine.destroy();
|
||||
}
|
||||
}
|
||||
ObjectNode result = JacksonUtil.newObjectNode();
|
||||
result.put("output", output);
|
||||
result.put("error", errorText);
|
||||
return result;
|
||||
return JacksonUtil.newObjectNode()
|
||||
.put("output", output)
|
||||
.put("error", errorText);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Export Rule Chains", notes = "Exports all tenant rule chains as one JSON." + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChains/export", params = {"limit"}, method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/ruleChains/export", params = {"limit"})
|
||||
public RuleChainData exportRuleChains(
|
||||
@Parameter(description = "A limit of rule chains to export.", required = true)
|
||||
@RequestParam("limit") int limit) throws ThingsboardException {
|
||||
@ -443,8 +417,7 @@ public class RuleChainController extends BaseController {
|
||||
|
||||
@ApiOperation(value = "Import Rule Chains", notes = "Imports all tenant rule chains as one JSON." + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChains/import", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PostMapping("/ruleChains/import")
|
||||
public List<RuleChainImportResult> importRuleChains(
|
||||
@Parameter(description = "A JSON value representing the rule chains.")
|
||||
@RequestBody RuleChainData ruleChainData,
|
||||
@ -454,12 +427,12 @@ public class RuleChainController extends BaseController {
|
||||
return ruleChainService.importTenantRuleChains(tenantId, ruleChainData, overwrite, tbRuleChainService::updateRuleNodeConfiguration);
|
||||
}
|
||||
|
||||
private String msgToOutput(TbMsg msg) throws Exception {
|
||||
private String msgToOutput(TbMsg msg) {
|
||||
JsonNode resultNode = convertMsgToOut(msg);
|
||||
return JacksonUtil.toString(resultNode);
|
||||
}
|
||||
|
||||
private String msgToOutput(List<TbMsg> msgs) throws Exception {
|
||||
private String msgToOutput(List<TbMsg> msgs) {
|
||||
JsonNode resultNode;
|
||||
if (msgs.size() > 1) {
|
||||
resultNode = JacksonUtil.newArrayNode();
|
||||
@ -473,7 +446,7 @@ public class RuleChainController extends BaseController {
|
||||
return JacksonUtil.toString(resultNode);
|
||||
}
|
||||
|
||||
private JsonNode convertMsgToOut(TbMsg msg) throws Exception {
|
||||
private JsonNode convertMsgToOut(TbMsg msg) {
|
||||
ObjectNode msgData = JacksonUtil.newObjectNode();
|
||||
if (!StringUtils.isEmpty(msg.getData())) {
|
||||
msgData.set("msg", JacksonUtil.toJsonNode(msg.getData()));
|
||||
@ -492,8 +465,7 @@ public class RuleChainController extends BaseController {
|
||||
"Third, once rule chain will be delivered to edge service, it's going to start processing messages locally. " +
|
||||
"\n\nOnly rule chain with type 'EDGE' can be assigned to edge." + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PostMapping("/edge/{edgeId}/ruleChain/{ruleChainId}")
|
||||
public RuleChain assignRuleChainToEdge(@PathVariable("edgeId") String strEdgeId,
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
checkParameter("edgeId", strEdgeId);
|
||||
@ -514,8 +486,7 @@ public class RuleChainController extends BaseController {
|
||||
EDGE_UNASSIGN_RECEIVE_STEP_DESCRIPTION +
|
||||
"Third, once 'unassign' command will be delivered to edge service, it's going to remove rule chain locally." + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/edge/{edgeId}/ruleChain/{ruleChainId}", method = RequestMethod.DELETE)
|
||||
@ResponseBody
|
||||
@DeleteMapping("/edge/{edgeId}/ruleChain/{ruleChainId}")
|
||||
public RuleChain unassignRuleChainFromEdge(@PathVariable("edgeId") String strEdgeId,
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
checkParameter("edgeId", strEdgeId);
|
||||
@ -530,9 +501,8 @@ public class RuleChainController extends BaseController {
|
||||
|
||||
@ApiOperation(value = "Get Edge Rule Chains (getEdgeRuleChains)",
|
||||
notes = "Returns a page of Rule Chains assigned to the specified edge. " + RULE_CHAIN_DESCRIPTION + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"}, method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@GetMapping(value = "/edge/{edgeId}/ruleChains", params = {"pageSize", "page"})
|
||||
public PageData<RuleChain> getEdgeRuleChains(
|
||||
@Parameter(description = EDGE_ID_PARAM_DESCRIPTION, required = true)
|
||||
@PathVariable(EDGE_ID) String strEdgeId,
|
||||
@ -557,9 +527,8 @@ public class RuleChainController extends BaseController {
|
||||
@ApiOperation(value = "Set Edge Template Root Rule Chain (setEdgeTemplateRootRuleChain)",
|
||||
notes = "Makes the rule chain to be root rule chain for any new edge that will be created. " +
|
||||
"Does not update root rule chain for already created edges. " + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/{ruleChainId}/edgeTemplateRoot", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@PostMapping("/ruleChain/{ruleChainId}/edgeTemplateRoot")
|
||||
public RuleChain setEdgeTemplateRootRuleChain(@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
checkParameter(RULE_CHAIN_ID, strRuleChainId);
|
||||
@ -572,8 +541,7 @@ public class RuleChainController extends BaseController {
|
||||
notes = "Makes the rule chain to be automatically assigned for any new edge that will be created. " +
|
||||
"Does not assign this rule chain for already created edges. " + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/{ruleChainId}/autoAssignToEdge", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
@PostMapping("/ruleChain/{ruleChainId}/autoAssignToEdge")
|
||||
public RuleChain setAutoAssignToEdgeRuleChain(@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
checkParameter(RULE_CHAIN_ID, strRuleChainId);
|
||||
@ -586,8 +554,7 @@ public class RuleChainController extends BaseController {
|
||||
notes = "Removes the rule chain from the list of rule chains that are going to be automatically assigned for any new edge that will be created. " +
|
||||
"Does not unassign this rule chain for already assigned edges. " + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/{ruleChainId}/autoAssignToEdge", method = RequestMethod.DELETE)
|
||||
@ResponseBody
|
||||
@DeleteMapping("/ruleChain/{ruleChainId}/autoAssignToEdge")
|
||||
public RuleChain unsetAutoAssignToEdgeRuleChain(@Parameter(description = RULE_CHAIN_ID_PARAM_DESCRIPTION)
|
||||
@PathVariable(RULE_CHAIN_ID) String strRuleChainId) throws ThingsboardException {
|
||||
checkParameter(RULE_CHAIN_ID, strRuleChainId);
|
||||
@ -599,9 +566,8 @@ public class RuleChainController extends BaseController {
|
||||
// TODO: @voba refactor this - add new config to edge rule chain to set it as auto-assign
|
||||
@ApiOperation(value = "Get Auto Assign To Edge Rule Chains (getAutoAssignToEdgeRuleChains)",
|
||||
notes = "Returns a list of Rule Chains that will be assigned to a newly created edge. " + RULE_CHAIN_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
@RequestMapping(value = "/ruleChain/autoAssignToEdgeRuleChains", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@PreAuthorize("hasAuthority('TENANT_ADMIN')")
|
||||
@GetMapping("/ruleChain/autoAssignToEdgeRuleChains")
|
||||
public List<RuleChain> getAutoAssignToEdgeRuleChains() throws ThingsboardException {
|
||||
TenantId tenantId = getCurrentUser().getTenantId();
|
||||
List<RuleChain> result = new ArrayList<>();
|
||||
@ -612,4 +578,5 @@ public class RuleChainController extends BaseController {
|
||||
}
|
||||
return checkNotNull(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user