|
|
|
@ -15,6 +15,9 @@ |
|
|
|
*/ |
|
|
|
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; |
|
|
|
@ -29,6 +32,8 @@ 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; |
|
|
|
import org.thingsboard.script.api.tbel.TbelInvokeService; |
|
|
|
import org.thingsboard.server.common.data.cf.CalculatedField; |
|
|
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|
|
|
import org.thingsboard.server.common.data.id.CalculatedFieldId; |
|
|
|
@ -38,16 +43,27 @@ 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.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.permission.Operation; |
|
|
|
|
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Objects; |
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
|
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.CF_TEXT_SEARCH_DESCRIPTION; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_ID_PARAM_DESCRIPTION; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.ENTITY_TYPE_PARAM_DESCRIPTION; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_START; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; |
|
|
|
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; |
|
|
|
|
|
|
|
@ -59,9 +75,29 @@ import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LI |
|
|
|
public class CalculatedFieldController extends BaseController { |
|
|
|
|
|
|
|
private final TbCalculatedFieldService tbCalculatedFieldService; |
|
|
|
private final TbelInvokeService tbelInvokeService; |
|
|
|
|
|
|
|
public static final String CALCULATED_FIELD_ID = "calculatedFieldId"; |
|
|
|
|
|
|
|
public static final int TIMEOUT = 20; |
|
|
|
|
|
|
|
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.entrySet()) { temp += element.getValue(); } var avgTemperature = temp / temperature.size(); var adjustedTemperature = avgTemperature + 0.1 * humidity; return { \\\"adjustedTemperature\\\": adjustedTemperature };\",\n" + |
|
|
|
" \"argNames\": [\"temperature\", \"humidity\"],\n" + |
|
|
|
" \"arguments\": {\n" + |
|
|
|
" \"temperature\": {\n" + |
|
|
|
" \"14327856345\": 22.4,\n" + |
|
|
|
" \"14327857298\": 21.9,\n" + |
|
|
|
" \"14327857510\": 22.0\n" + |
|
|
|
" },\n" + |
|
|
|
" \"humidity\": 42\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 + |
|
|
|
"The newly created Calculated Field Id will be present in the response. " + |
|
|
|
@ -128,4 +164,52 @@ public class CalculatedFieldController extends BaseController { |
|
|
|
tbCalculatedFieldService.delete(calculatedField, getCurrentUser()); |
|
|
|
} |
|
|
|
|
|
|
|
@ApiOperation(value = "Test Script expression", |
|
|
|
notes = TEST_SCRIPT_EXPRESSION + TENANT_AUTHORITY_PARAGRAPH) |
|
|
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
|
|
|
@RequestMapping(value = "/calculatedField/testScript", method = RequestMethod.POST) |
|
|
|
@ResponseBody |
|
|
|
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(); |
|
|
|
String[] argNames = JacksonUtil.treeToValue(inputParams.get("argNames"), String[].class); |
|
|
|
Map<String, Object> arguments = Objects.requireNonNullElse( |
|
|
|
JacksonUtil.convertValue(inputParams.get("arguments"), new TypeReference<Map<String, Object>>() { |
|
|
|
}), |
|
|
|
Collections.emptyMap() |
|
|
|
); |
|
|
|
|
|
|
|
String output = ""; |
|
|
|
String errorText = ""; |
|
|
|
|
|
|
|
try { |
|
|
|
if (tbelInvokeService == null) { |
|
|
|
throw new IllegalArgumentException("TBEL script engine is disabled!"); |
|
|
|
} |
|
|
|
|
|
|
|
CalculatedFieldScriptEngine calculatedFieldScriptEngine = new CalculatedFieldTbelScriptEngine( |
|
|
|
getTenantId(), |
|
|
|
tbelInvokeService, |
|
|
|
expression, |
|
|
|
argNames |
|
|
|
); |
|
|
|
|
|
|
|
Object[] args = Arrays.stream(argNames) |
|
|
|
.map(arguments::get) |
|
|
|
.toArray(); |
|
|
|
|
|
|
|
JsonNode json = calculatedFieldScriptEngine.executeJsonAsync(args).get(TIMEOUT, TimeUnit.SECONDS); |
|
|
|
output = JacksonUtil.toString(json); |
|
|
|
} catch (Exception e) { |
|
|
|
log.error("Error evaluating expression", e); |
|
|
|
errorText = e.getMessage(); |
|
|
|
} |
|
|
|
|
|
|
|
ObjectNode result = JacksonUtil.newObjectNode(); |
|
|
|
result.put("output", output); |
|
|
|
result.put("error", errorText); |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|