Merge branch 'master' of github.com:thingsboard/thingsboard into versions-upgrade
This commit is contained in:
commit
947c2c2ad6
@ -37,6 +37,7 @@ import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.thingsboard.common.util.DonAsynchron;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.cluster.TbClusterService;
|
||||
@ -458,6 +459,8 @@ public abstract class BaseController {
|
||||
return new ThingsboardException(exception, ThingsboardErrorCode.DATABASE);
|
||||
} else if (exception instanceof EntityVersionMismatchException) {
|
||||
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.VERSION_CONFLICT);
|
||||
} else if (exception instanceof MethodArgumentTypeMismatchException) {
|
||||
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.BAD_REQUEST_PARAMS);
|
||||
}
|
||||
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.GENERAL);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest;
|
||||
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
|
||||
@ -33,6 +34,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.thingsboard.server.common.data.query.EntityKeyType.TIME_SERIES;
|
||||
|
||||
@ -208,6 +210,15 @@ public class TelemetryControllerTest extends AbstractControllerTest {
|
||||
doPostAsync("/api/plugins/telemetry/DEVICE/" + device.getId() + "/timeseries/smth", invalidRequestBody, String.class, status().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadRequestReturnedWhenMethodArgumentTypeMismatch() throws Exception {
|
||||
loginTenantAdmin();
|
||||
String content = "{\"key\": \"value\"}";
|
||||
doPost("/api/plugins/telemetry/DEVICE/20b559f5-849f-4361-b4f6-b6d0b76687e9/INVALID_SCOPE", content, (String) null)
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(result -> assertThat(result.getResolvedException()).isInstanceOf(MethodArgumentTypeMismatchException.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyKeyIsProhibited() throws Exception {
|
||||
loginTenantAdmin();
|
||||
|
||||
@ -140,6 +140,7 @@
|
||||
"tinymce": "6.8.5",
|
||||
"rollup": "4.22.4",
|
||||
"@babel/core": "7.25.2",
|
||||
"esbuild": "0.23.0"
|
||||
"esbuild": "0.23.0",
|
||||
"jquery.terminal/coveralls-next/form-data": "4.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@ -3740,6 +3740,14 @@ cacache@^18.0.0:
|
||||
tar "^6.1.11"
|
||||
unique-filename "^3.0.0"
|
||||
|
||||
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
|
||||
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
function-bind "^1.1.2"
|
||||
|
||||
call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
|
||||
@ -4788,6 +4796,15 @@ domutils@^3.0.1:
|
||||
domelementtype "^2.3.0"
|
||||
domhandler "^5.0.3"
|
||||
|
||||
dunder-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
|
||||
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
gopd "^1.2.0"
|
||||
|
||||
earcut@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/earcut/-/earcut-3.0.1.tgz#f60b3f671c5657cca9d3e131c5527c5dde00ef38"
|
||||
@ -4968,6 +4985,11 @@ es-define-property@^1.0.0:
|
||||
dependencies:
|
||||
get-intrinsic "^1.2.4"
|
||||
|
||||
es-define-property@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
|
||||
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
|
||||
|
||||
es-errors@^1.2.1, es-errors@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||
@ -4985,6 +5007,13 @@ es-object-atoms@^1.0.0:
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
|
||||
es-object-atoms@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
|
||||
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
|
||||
es-set-tostringtag@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777"
|
||||
@ -4994,6 +5023,16 @@ es-set-tostringtag@^2.0.3:
|
||||
has-tostringtag "^1.0.2"
|
||||
hasown "^2.0.1"
|
||||
|
||||
es-set-tostringtag@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
|
||||
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
get-intrinsic "^1.2.6"
|
||||
has-tostringtag "^1.0.2"
|
||||
hasown "^2.0.2"
|
||||
|
||||
es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763"
|
||||
@ -5508,13 +5547,15 @@ foreground-child@^3.1.0:
|
||||
cross-spawn "^7.0.0"
|
||||
signal-exit "^4.0.1"
|
||||
|
||||
form-data@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||
form-data@4.0.0, form-data@4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4"
|
||||
integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
es-set-tostringtag "^2.1.0"
|
||||
hasown "^2.0.2"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formdata-polyfill@^4.0.10:
|
||||
@ -5635,6 +5676,30 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@
|
||||
has-symbols "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
|
||||
get-intrinsic@^1.2.6:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
|
||||
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.2"
|
||||
es-define-property "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
es-object-atoms "^1.1.1"
|
||||
function-bind "^1.1.2"
|
||||
get-proto "^1.0.1"
|
||||
gopd "^1.2.0"
|
||||
has-symbols "^1.1.0"
|
||||
hasown "^2.0.2"
|
||||
math-intrinsics "^1.1.0"
|
||||
|
||||
get-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
|
||||
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
|
||||
dependencies:
|
||||
dunder-proto "^1.0.1"
|
||||
es-object-atoms "^1.0.0"
|
||||
|
||||
get-stream@^6.0.0, get-stream@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
|
||||
@ -5750,6 +5815,11 @@ gopd@^1.0.1:
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
gopd@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
|
||||
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
||||
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
||||
@ -5807,6 +5877,11 @@ has-symbols@^1.0.2, has-symbols@^1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
|
||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
||||
|
||||
has-symbols@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
|
||||
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
|
||||
|
||||
has-tostringtag@^1.0.0, has-tostringtag@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
|
||||
@ -6970,6 +7045,11 @@ marked@~12.0.2:
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-12.0.2.tgz#b31578fe608b599944c69807b00f18edab84647e"
|
||||
integrity sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==
|
||||
|
||||
math-intrinsics@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
|
||||
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user