Browse Source

Update VC for alarm rules CFs

pull/14463/head
Viacheslav Klimov 10 months ago
parent
commit
c5050890ba
  1. 9
      application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java
  2. 9
      application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java
  3. 31
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  4. 20
      application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java
  5. 187
      application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java
  6. 4
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/AlarmRule.java
  7. 2
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmCondition.java
  8. 4
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/TbelAlarmConditionExpression.java

9
application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/DefaultEntityExportService.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.service.sync.ie.exporting.impl; package org.thingsboard.server.service.sync.ie.exporting.impl;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
@ -24,6 +25,7 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.ExportableEntity;
import org.thingsboard.server.common.data.HasVersion; import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -170,6 +172,13 @@ public class DefaultEntityExportService<I extends EntityId, E extends Exportable
}); });
} }
} }
if (calculatedField.getConfiguration() instanceof AlarmCalculatedFieldConfiguration alarmCfConfig) {
alarmCfConfig.getAllRules().map(Pair::getValue).forEach(rule -> {
if (rule.getDashboardId() != null) {
rule.setDashboardId(getExternalIdOrElseInternal(ctx, rule.getDashboardId()));
}
});
}
}); });
return calculatedFields; return calculatedFields;
} }

9
application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/BaseEntityImportService.java

@ -22,6 +22,7 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
@ -35,6 +36,7 @@ import org.thingsboard.server.common.data.HasVersion;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.geofencing.GeofencingCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -338,6 +340,13 @@ public abstract class BaseEntityImportService<I extends EntityId, E extends Expo
}); });
} }
} }
if (calculatedField.getConfiguration() instanceof AlarmCalculatedFieldConfiguration alarmCfConfig) {
alarmCfConfig.getAllRules().map(Pair::getValue).forEach(rule -> {
if (rule.getDashboardId() != null) {
rule.setDashboardId(idProvider.getInternalId(rule.getDashboardId(), ctx.isFinalImportAttempt()));
}
});
}
}).toList(); }).toList();
for (CalculatedField existingField : existing) { for (CalculatedField existingField : existing) {

31
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -84,6 +84,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EventInfo; import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest; import org.thingsboard.server.common.data.SaveDeviceWithCredentialsRequest;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
@ -93,6 +94,7 @@ import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldInfo;
import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration; import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration;
import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration;
@ -1207,8 +1209,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
Map<CalculatedFieldId, CalculatedFieldState> statesMap = (Map<CalculatedFieldId, CalculatedFieldState>) ReflectionTestUtils.getField(processor, "states"); Map<CalculatedFieldId, CalculatedFieldState> statesMap = (Map<CalculatedFieldId, CalculatedFieldState>) ReflectionTestUtils.getField(processor, "states");
Awaitility.await("CF state for entity actor ready to refresh dynamic arguments").atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> { Awaitility.await("CF state for entity actor ready to refresh dynamic arguments").atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> {
CalculatedFieldState calculatedFieldState = statesMap.get(cfId); CalculatedFieldState calculatedFieldState = statesMap.get(cfId);
boolean isReady = calculatedFieldState != null && ((GeofencingCalculatedFieldState) calculatedFieldState).getLastDynamicArgumentsRefreshTs() boolean isReady = calculatedFieldState != null && ((GeofencingCalculatedFieldState) calculatedFieldState).getLastDynamicArgumentsRefreshTs() <
< System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(scheduledUpdateInterval); System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(scheduledUpdateInterval);
log.warn("entityId {}, cfId {}, state ready to refresh == {}", entityId, cfId, isReady); log.warn("entityId {}, cfId {}, state ready to refresh == {}", entityId, cfId, isReady);
return isReady; return isReady;
}); });
@ -1399,7 +1401,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
protected List<Job> findJobs(List<JobType> types, List<UUID> entities) throws Exception { protected List<Job> findJobs(List<JobType> types, List<UUID> entities) throws Exception {
return doGetTypedWithPageLink("/api/jobs?types=" + types.stream().map(Enum::name).collect(Collectors.joining(",")) + return doGetTypedWithPageLink("/api/jobs?types=" + types.stream().map(Enum::name).collect(Collectors.joining(",")) +
"&entities=" + entities.stream().map(UUID::toString).collect(Collectors.joining(",")) + "&", "&entities=" + entities.stream().map(UUID::toString).collect(Collectors.joining(",")) + "&",
new TypeReference<PageData<Job>>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData(); new TypeReference<PageData<Job>>() {}, new PageLink(100, 0, null, new SortOrder("createdTime", SortOrder.Direction.DESC))).getData();
} }
@ -1413,12 +1415,12 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
protected void postTelemetry(EntityId entityId, String payload) throws Exception { protected void postTelemetry(EntityId entityId, String payload) throws Exception {
doPostAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() + doPostAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() +
"/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(payload), 30_000L).andExpect(status().isOk()); "/timeseries/" + DataConstants.SERVER_SCOPE, JacksonUtil.toJsonNode(payload), 30_000L).andExpect(status().isOk());
} }
protected void postAttributes(EntityId entityId, AttributeScope scope, String payload) throws Exception { protected void postAttributes(EntityId entityId, AttributeScope scope, String payload) throws Exception {
doPostAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() + doPostAsync("/api/plugins/telemetry/" + entityId.getEntityType() + "/" + entityId.getId() +
"/attributes/" + scope, JacksonUtil.toJsonNode(payload), 30_000L).andExpect(status().isOk()); "/attributes/" + scope, JacksonUtil.toJsonNode(payload), 30_000L).andExpect(status().isOk());
} }
protected CalculatedField saveCalculatedField(CalculatedField calculatedField) { protected CalculatedField saveCalculatedField(CalculatedField calculatedField) {
@ -1427,7 +1429,24 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
protected PageData<CalculatedField> getEntityCalculatedFields(EntityId entityId, CalculatedFieldType type, PageLink pageLink) throws Exception { protected PageData<CalculatedField> getEntityCalculatedFields(EntityId entityId, CalculatedFieldType type, PageLink pageLink) throws Exception {
return doGetTypedWithPageLink("/api/" + entityId.getEntityType() + "/" + entityId.getId() + "/calculatedFields" + return doGetTypedWithPageLink("/api/" + entityId.getEntityType() + "/" + entityId.getId() + "/calculatedFields" +
(type != null ? "?type=" + type.name() + "&" : "?"), new TypeReference<>() {}, pageLink); (type != null ? "?type=" + type.name() + "&" : "?"), new TypeReference<>() {}, pageLink);
}
protected PageData<String> getCalculatedFieldNames(CalculatedFieldType type, PageLink pageLink) throws Exception {
return doGetTypedWithPageLink("/api/calculatedFields/names?type=" + type + "&",
new TypeReference<PageData<String>>() {}, pageLink);
}
protected List<CalculatedFieldInfo> getCalculatedFields(CalculatedFieldType type,
EntityType entityType,
List<UUID> entities,
List<String> names) throws Exception {
return doGetTypedWithPageLink("/api/calculatedFields?type=" + type + "&" +
(entityType != null ? "entityType=" + entityType + "&" : "") +
(entities != null ? "entities=" + String.join(",",
entities.stream().map(UUID::toString).toList()) + "&" : "") +
(names != null ? names.stream().map(name -> "name=" + name + "&").collect(Collectors.joining("")) : ""),
new TypeReference<PageData<CalculatedFieldInfo>>() {}, new PageLink(10)).getData();
} }
protected PageData<EventInfo> getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception { protected PageData<EventInfo> getDebugEvents(TenantId tenantId, EntityId entityId, int limit) throws Exception {

20
application/src/test/java/org/thingsboard/server/controller/CalculatedFieldControllerTest.java

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -56,9 +55,7 @@ import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
@ -295,23 +292,6 @@ public class CalculatedFieldControllerTest extends AbstractControllerTest {
assertThat(names.getData()).containsOnly(deviceCalculatedField.getName()); assertThat(names.getData()).containsOnly(deviceCalculatedField.getName());
} }
private PageData<String> getCalculatedFieldNames(CalculatedFieldType type, PageLink pageLink) throws Exception {
return doGetTypedWithPageLink("/api/calculatedFields/names?type=" + type + "&",
new TypeReference<PageData<String>>() {}, pageLink);
}
private List<CalculatedFieldInfo> getCalculatedFields(CalculatedFieldType type,
EntityType entityType,
List<UUID> entities,
List<String> names) throws Exception {
return doGetTypedWithPageLink("/api/calculatedFields?type=" + type + "&" +
(entityType != null ? "entityType=" + entityType + "&" : "") +
(entities != null ? "entities=" + String.join(",",
entities.stream().map(UUID::toString).toList()) + "&" : "") +
(names != null ? names.stream().map(name -> "name=" + name + "&").collect(Collectors.joining("")) : ""),
new TypeReference<PageData<CalculatedFieldInfo>>() {}, new PageLink(10)).getData();
}
@Test @Test
public void testDeleteCalculatedField() throws Exception { public void testDeleteCalculatedField() throws Exception {
Device testDevice = createDevice("Test device", "1234567890"); Device testDevice = createDevice("Test device", "1234567890");

187
application/src/test/java/org/thingsboard/server/service/sync/vc/VersionControlTest.java

@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode; import com.fasterxml.jackson.databind.node.TextNode;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -45,10 +46,15 @@ import org.thingsboard.server.common.data.TbResource;
import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.rule.AlarmRule;
import org.thingsboard.server.common.data.alarm.rule.condition.SimpleAlarmCondition;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.TbelAlarmConditionExpression;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.cf.CalculatedField;
import org.thingsboard.server.common.data.cf.CalculatedFieldType; import org.thingsboard.server.common.data.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.cf.configuration.AlarmCalculatedFieldConfiguration;
import org.thingsboard.server.common.data.cf.configuration.Argument; import org.thingsboard.server.common.data.cf.configuration.Argument;
import org.thingsboard.server.common.data.cf.configuration.ArgumentType; import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration; import org.thingsboard.server.common.data.cf.configuration.CalculatedFieldConfiguration;
@ -306,6 +312,54 @@ public class VersionControlTest extends AbstractControllerTest {
checkImportedOtaPackageData(software, importedSoftwareOta); checkImportedOtaPackageData(software, importedSoftwareOta);
} }
@Test
public void testDeviceVc_withAlarmRules_betweenTenants() throws Exception {
DeviceProfile deviceProfile = createDeviceProfile(null, null, "Device profile of tenant 1");
Dashboard dashboard = createDashboard(null, "Mobile dashboard");
createAlarmRule(deviceProfile.getId(), "Profile alarm rule", dashboard.getId());
Device device = createDevice(deviceProfile.getId(), "Device of tenant 1", "test1");
createAlarmRule(device.getId(), "Device alarm rule", dashboard.getId());
String version = createVersion("devices, profiles and dashboards", EntityType.DEVICE, EntityType.DEVICE_PROFILE, EntityType.DASHBOARD);
loginTenant2();
Map<EntityType, EntityTypeLoadResult> result = loadVersion(version, config -> {
config.setLoadCredentials(false);
}, EntityType.DEVICE, EntityType.DEVICE_PROFILE, EntityType.DASHBOARD);
assertThat(result.get(EntityType.DEVICE).getCreated()).isEqualTo(1);
assertThat(result.get(EntityType.DEVICE_PROFILE).getCreated()).isEqualTo(1);
assertThat(result.get(EntityType.DASHBOARD).getCreated()).isEqualTo(1);
Device importedDevice = findDevice(device.getName());
checkImportedEntity(tenantId1, device, tenantId2, importedDevice);
checkImportedDeviceData(device, importedDevice);
DeviceProfile importedDeviceProfile = findDeviceProfile(deviceProfile.getName());
checkImportedEntity(tenantId1, deviceProfile, tenantId2, importedDeviceProfile);
checkImportedDeviceProfileData(deviceProfile, importedDeviceProfile);
assertThat(importedDevice.getDeviceProfileId()).isEqualTo(importedDeviceProfile.getId());
Dashboard importedDashboard = findDashboard(dashboard.getName());
checkImportedEntity(tenantId1, dashboard, tenantId2, importedDashboard);
checkImportedDashboardData(dashboard, importedDashboard);
getCalculatedFields(CalculatedFieldType.ALARM, EntityType.DEVICE_PROFILE,
List.of(importedDeviceProfile.getUuidId()), null).forEach(alarmRuleCf -> {
assertThat(alarmRuleCf.getName()).isEqualTo("Profile alarm rule");
AlarmCalculatedFieldConfiguration config = (AlarmCalculatedFieldConfiguration) alarmRuleCf.getConfiguration();
config.getAllRules().map(Pair::getValue).forEach(alarmRule -> {
assertThat(alarmRule.getDashboardId()).isEqualTo(importedDashboard.getId());
});
});
getCalculatedFields(CalculatedFieldType.ALARM, EntityType.DEVICE_PROFILE,
List.of(importedDevice.getUuidId()), null).forEach(alarmRuleCf -> {
assertThat(alarmRuleCf.getName()).isEqualTo("Device alarm rule");
AlarmCalculatedFieldConfiguration config = (AlarmCalculatedFieldConfiguration) alarmRuleCf.getConfiguration();
config.getAllRules().map(Pair::getValue).forEach(alarmRule -> {
assertThat(alarmRule.getDashboardId()).isEqualTo(importedDashboard.getId());
});
});
}
@Test @Test
public void testDashboardVc_betweenTenants() throws Exception { public void testDashboardVc_betweenTenants() throws Exception {
Dashboard dashboard = createDashboard(null, "Dashboard of tenant 1"); Dashboard dashboard = createDashboard(null, "Dashboard of tenant 1");
@ -368,42 +422,42 @@ public class VersionControlTest extends AbstractControllerTest {
String aliasId = "23c4185d-1497-9457-30b2-6d91e69a5b2c"; String aliasId = "23c4185d-1497-9457-30b2-6d91e69a5b2c";
String unknownUuid = "ea0dc8b0-3d85-11ed-9200-77fc04fa14fa"; String unknownUuid = "ea0dc8b0-3d85-11ed-9200-77fc04fa14fa";
String entityAliases = "{\n" + String entityAliases = "{\n" +
"\"" + aliasId + "\": {\n" + "\"" + aliasId + "\": {\n" +
"\"alias\": \"assets\",\n" + "\"alias\": \"assets\",\n" +
"\"filter\": {\n" + "\"filter\": {\n" +
" \"entityList\": [\n" + " \"entityList\": [\n" +
" \"" + asset1.getId() + "\",\n" + " \"" + asset1.getId() + "\",\n" +
" \"" + asset2.getId() + "\",\n" + " \"" + asset2.getId() + "\",\n" +
" \"" + tenantId1.getId() + "\",\n" + " \"" + tenantId1.getId() + "\",\n" +
" \"" + existingDeviceProfile.getId() + "\",\n" + " \"" + existingDeviceProfile.getId() + "\",\n" +
" \"" + unknownUuid + "\"\n" + " \"" + unknownUuid + "\"\n" +
" ],\n" + " ],\n" +
" \"id\":\"" + asset1.getId() + "\",\n" + " \"id\":\"" + asset1.getId() + "\",\n" +
" \"resolveMultiple\": true\n" + " \"resolveMultiple\": true\n" +
"},\n" + "},\n" +
"\"id\": \"" + aliasId + "\"\n" + "\"id\": \"" + aliasId + "\"\n" +
"}\n" + "}\n" +
"}"; "}";
String widgetId = "ea8f34a0-264a-f11f-cde3-05201bb4ff4b"; String widgetId = "ea8f34a0-264a-f11f-cde3-05201bb4ff4b";
String actionId = "4a8e6efa-3e68-fa59-7feb-d83366130cae"; String actionId = "4a8e6efa-3e68-fa59-7feb-d83366130cae";
String widgets = "{\n" + String widgets = "{\n" +
" \"" + widgetId + "\": {\n" + " \"" + widgetId + "\": {\n" +
" \"config\": {\n" + " \"config\": {\n" +
" \"actions\": {\n" + " \"actions\": {\n" +
" \"rowClick\": [\n" + " \"rowClick\": [\n" +
" {\n" + " {\n" +
" \"name\": \"go to dashboard\",\n" + " \"name\": \"go to dashboard\",\n" +
" \"targetDashboardId\": \"" + otherDashboard.getId() + "\",\n" + " \"targetDashboardId\": \"" + otherDashboard.getId() + "\",\n" +
" \"id\": \"" + actionId + "\"\n" + " \"id\": \"" + actionId + "\"\n" +
" }\n" + " }\n" +
" ]\n" + " ]\n" +
" }\n" + " }\n" +
" },\n" + " },\n" +
" \"row\": 0,\n" + " \"row\": 0,\n" +
" \"col\": 0,\n" + " \"col\": 0,\n" +
" \"id\": \"" + widgetId + "\"\n" + " \"id\": \"" + widgetId + "\"\n" +
" }\n" + " }\n" +
"}"; "}";
ObjectNode dashboardConfiguration = JacksonUtil.newObjectNode(); ObjectNode dashboardConfiguration = JacksonUtil.newObjectNode();
dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases)); dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases));
@ -499,11 +553,12 @@ public class VersionControlTest extends AbstractControllerTest {
generatorNodeConfig.setMsgCount(1); generatorNodeConfig.setMsgCount(1);
generatorNodeConfig.setScriptLang(ScriptLanguage.JS); generatorNodeConfig.setScriptLang(ScriptLanguage.JS);
UUID someUuid = UUID.randomUUID(); UUID someUuid = UUID.randomUUID();
generatorNodeConfig.setJsScript("var msg = { temp: 42, humidity: 77 };\n" + generatorNodeConfig.setJsScript("""
"var metadata = { data: 40 };\n" + var msg = { temp: 42, humidity: 77 };
"var msgType = \"POST_TELEMETRY_REQUEST\";\n" + var metadata = { data: 40 };
"var someUuid = \"" + someUuid + "\";\n" + var msgType = "POST_TELEMETRY_REQUEST";
"return { msg: msg, metadata: metadata, msgType: msgType };"); var someUuid = "%s";
return { msg: msg, metadata: metadata, msgType: msgType };""".formatted(someUuid));
generatorNode.setConfiguration(JacksonUtil.valueToTree(generatorNodeConfig)); generatorNode.setConfiguration(JacksonUtil.valueToTree(generatorNodeConfig));
nodes.add(generatorNode); nodes.add(generatorNode);
metaData.setNodes(nodes); metaData.setNodes(nodes);
@ -1018,20 +1073,21 @@ public class VersionControlTest extends AbstractControllerTest {
protected Dashboard createDashboard(CustomerId customerId, String name, AssetId assetForEntityAlias) { protected Dashboard createDashboard(CustomerId customerId, String name, AssetId assetForEntityAlias) {
Dashboard dashboard = createDashboard(customerId, name); Dashboard dashboard = createDashboard(customerId, name);
String entityAliases = "{\n" + String entityAliases = """
"\t\"23c4185d-1497-9457-30b2-6d91e69a5b2c\": {\n" + {
"\t\t\"alias\": \"assets\",\n" + "23c4185d-1497-9457-30b2-6d91e69a5b2c": {
"\t\t\"filter\": {\n" + "alias": "assets",
"\t\t\t\"entityList\": [\n" + "filter": {
"\t\t\t\t\"" + assetForEntityAlias.getId().toString() + "\"\n" + "entityList": [
"\t\t\t],\n" + "%s"
"\t\t\t\"entityType\": \"ASSET\",\n" + ],
"\t\t\t\"resolveMultiple\": true,\n" + "entityType": "ASSET",
"\t\t\t\"type\": \"entityList\"\n" + "resolveMultiple": true,
"\t\t},\n" + "type": "entityList"
"\t\t\"id\": \"23c4185d-1497-9457-30b2-6d91e69a5b2c\"\n" + },
"\t}\n" + "id": "23c4185d-1497-9457-30b2-6d91e69a5b2c"
"}"; }
}""".formatted(assetForEntityAlias.getId().toString());
ObjectNode dashboardConfiguration = JacksonUtil.newObjectNode(); ObjectNode dashboardConfiguration = JacksonUtil.newObjectNode();
dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases)); dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases));
dashboardConfiguration.set("description", new TextNode("hallo")); dashboardConfiguration.set("description", new TextNode("hallo"));
@ -1134,6 +1190,33 @@ public class VersionControlTest extends AbstractControllerTest {
return doPost("/api/calculatedField", calculatedField, CalculatedField.class); return doPost("/api/calculatedField", calculatedField, CalculatedField.class);
} }
private CalculatedField createAlarmRule(EntityId entityId, String alarmType, DashboardId mobileDashboardId) {
Argument temperatureArgument = new Argument();
temperatureArgument.setRefEntityKey(new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null));
temperatureArgument.setDefaultValue("0");
Map<String, Argument> arguments = Map.of(
"temperature", temperatureArgument
);
CalculatedField calculatedField = new CalculatedField();
calculatedField.setEntityId(entityId);
calculatedField.setName(alarmType);
calculatedField.setType(CalculatedFieldType.ALARM);
AlarmCalculatedFieldConfiguration configuration = new AlarmCalculatedFieldConfiguration();
configuration.setArguments(arguments);
SimpleAlarmCondition createCondition = new SimpleAlarmCondition();
createCondition.setExpression(new TbelAlarmConditionExpression("return temperature >= 50;"));
configuration.setCreateRules(Map.of(
AlarmSeverity.CRITICAL, new AlarmRule(createCondition, "", mobileDashboardId)
));
SimpleAlarmCondition clearCondition = new SimpleAlarmCondition();
clearCondition.setExpression(new TbelAlarmConditionExpression("return temperature < 50;"));
configuration.setClearRule(new AlarmRule(clearCondition, "", mobileDashboardId));
calculatedField.setConfiguration(configuration);
calculatedField.setDebugSettings(DebugSettings.all());
return saveCalculatedField(calculatedField);
}
private CalculatedFieldConfiguration getCalculatedFieldConfig(EntityId referencedEntityId) { private CalculatedFieldConfiguration getCalculatedFieldConfig(EntityId referencedEntityId) {
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration(); SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration();

4
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/AlarmRule.java

@ -18,11 +18,15 @@ package org.thingsboard.server.common.data.alarm.rule;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.alarm.rule.condition.AlarmCondition; import org.thingsboard.server.common.data.alarm.rule.condition.AlarmCondition;
import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DashboardId;
@Data @Data
@AllArgsConstructor
@NoArgsConstructor
public class AlarmRule { public class AlarmRule {
@Valid @Valid

2
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/AlarmCondition.java

@ -22,9 +22,9 @@ import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue; import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionExpression; import org.thingsboard.server.common.data.alarm.rule.condition.expression.AlarmConditionExpression;
import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AlarmSchedule; import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AlarmSchedule;
import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AnyTimeSchedule; import org.thingsboard.server.common.data.alarm.rule.condition.schedule.AnyTimeSchedule;

4
common/data/src/main/java/org/thingsboard/server/common/data/alarm/rule/condition/expression/TbelAlarmConditionExpression.java

@ -16,9 +16,13 @@
package org.thingsboard.server.common.data.alarm.rule.condition.expression; package org.thingsboard.server.common.data.alarm.rule.condition.expression;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
@Data @Data
@AllArgsConstructor
@NoArgsConstructor
public class TbelAlarmConditionExpression implements AlarmConditionExpression { public class TbelAlarmConditionExpression implements AlarmConditionExpression {
@NotBlank @NotBlank

Loading…
Cancel
Save