CalculatedField functionality support for Edge
- added test
This commit is contained in:
parent
d45cbcfcbd
commit
93948bf64b
@ -565,7 +565,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
|
||||
protected Device saveDeviceOnCloudAndVerifyDeliveryToEdge() throws Exception {
|
||||
// create device and assign to edge
|
||||
Device savedDevice = saveDevice(StringUtils.randomAlphanumeric(15), thermostatDeviceProfile.getName());
|
||||
edgeImitator.expectMessageAmount(2); // device and device profile messages
|
||||
edgeImitator.expectMessageAmount(3); // device and device profile messages and device credentials
|
||||
doPost("/api/edge/" + edge.getUuidId()
|
||||
+ "/device/" + savedDevice.getUuidId(), Device.class);
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
@ -0,0 +1,228 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.edge;
|
||||
|
||||
import com.datastax.oss.driver.api.core.uuid.Uuids;
|
||||
import com.google.protobuf.AbstractMessage;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedField;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
|
||||
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.Output;
|
||||
import org.thingsboard.server.common.data.cf.configuration.OutputType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey;
|
||||
import org.thingsboard.server.common.data.cf.configuration.SimpleCalculatedFieldConfiguration;
|
||||
import org.thingsboard.server.common.data.debug.DebugSettings;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||
import org.thingsboard.server.gen.edge.v1.CalculatedFieldUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
|
||||
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@DaoSqlTest
|
||||
public class CalculatedFieldEdgeTest extends AbstractEdgeTest {
|
||||
private static final String DEFAULT_CF_NAME = "Edge Test CalculatedField";
|
||||
private static final String UPDATED_CF_NAME = "Updated Edge Test CalculatedField";
|
||||
|
||||
@Test
|
||||
public void testCalculatedField_create_update_delete() throws Exception {
|
||||
Device savedDevice = saveDeviceOnCloudAndVerifyDeliveryToEdge();
|
||||
|
||||
// create calculatedField
|
||||
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration();
|
||||
CalculatedField calculatedField = createSimpleCalculatedField(savedDevice.getId(), config);
|
||||
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class);
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
|
||||
Assert.assertTrue(latestMessage instanceof CalculatedFieldUpdateMsg);
|
||||
CalculatedFieldUpdateMsg calculatedFieldUpdateMsg = (CalculatedFieldUpdateMsg) latestMessage;
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, calculatedFieldUpdateMsg.getMsgType());
|
||||
Assert.assertEquals(savedCalculatedField.getUuidId().getMostSignificantBits(), calculatedFieldUpdateMsg.getIdMSB());
|
||||
Assert.assertEquals(savedCalculatedField.getUuidId().getLeastSignificantBits(), calculatedFieldUpdateMsg.getIdLSB());
|
||||
CalculatedField calculatedFieldFromMsg = JacksonUtil.fromString(calculatedFieldUpdateMsg.getEntity(), CalculatedField.class, true);
|
||||
Assert.assertNotNull(calculatedFieldFromMsg);
|
||||
|
||||
Assert.assertEquals(DEFAULT_CF_NAME, calculatedFieldFromMsg.getName());
|
||||
Assert.assertEquals(savedDevice.getId(), calculatedFieldFromMsg.getEntityId());
|
||||
Assert.assertEquals(config, calculatedFieldFromMsg.getConfiguration());
|
||||
|
||||
// update calculatedField
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
savedCalculatedField.setName(UPDATED_CF_NAME);
|
||||
savedCalculatedField = doPost("/api/calculatedField", savedCalculatedField, CalculatedField.class);
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
latestMessage = edgeImitator.getLatestMessage();
|
||||
Assert.assertTrue(latestMessage instanceof CalculatedFieldUpdateMsg);
|
||||
calculatedFieldUpdateMsg = (CalculatedFieldUpdateMsg) latestMessage;
|
||||
calculatedFieldFromMsg = JacksonUtil.fromString(calculatedFieldUpdateMsg.getEntity(), CalculatedField.class, true);
|
||||
Assert.assertNotNull(calculatedFieldFromMsg);
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, calculatedFieldUpdateMsg.getMsgType());
|
||||
Assert.assertEquals(UPDATED_CF_NAME, calculatedFieldFromMsg.getName());
|
||||
|
||||
// delete calculatedField
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
doDelete("/api/calculatedField/" + savedCalculatedField.getUuidId())
|
||||
.andExpect(status().isOk());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
latestMessage = edgeImitator.getLatestMessage();
|
||||
Assert.assertTrue(latestMessage instanceof CalculatedFieldUpdateMsg);
|
||||
calculatedFieldUpdateMsg = (CalculatedFieldUpdateMsg) latestMessage;
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, calculatedFieldUpdateMsg.getMsgType());
|
||||
Assert.assertEquals(savedCalculatedField.getUuidId().getMostSignificantBits(), calculatedFieldUpdateMsg.getIdMSB());
|
||||
Assert.assertEquals(savedCalculatedField.getUuidId().getLeastSignificantBits(), calculatedFieldUpdateMsg.getIdLSB());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendCalculatedFieldToCloud() throws Exception {
|
||||
Device savedDevice = saveDeviceOnCloudAndVerifyDeliveryToEdge();
|
||||
|
||||
// create calculatedField
|
||||
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration();
|
||||
CalculatedField calculatedField = createSimpleCalculatedField(savedDevice.getId(), config);
|
||||
UUID uuid = Uuids.timeBased();
|
||||
UplinkMsg uplinkMsg = getUplinkMsg(uuid, calculatedField, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
|
||||
|
||||
checkCalculatedFieldOnCloud(uplinkMsg, uuid, calculatedField.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCalculatedFieldNameOnCloud() throws Exception {
|
||||
Device savedDevice = saveDeviceOnCloudAndVerifyDeliveryToEdge();
|
||||
|
||||
// create calculatedField
|
||||
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration();
|
||||
CalculatedField calculatedField = createSimpleCalculatedField(savedDevice.getId(), config);
|
||||
UUID uuid = Uuids.timeBased();
|
||||
UplinkMsg uplinkMsg = getUplinkMsg(uuid, calculatedField, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
|
||||
|
||||
checkCalculatedFieldOnCloud(uplinkMsg, uuid, calculatedField.getName());
|
||||
|
||||
calculatedField.setName(UPDATED_CF_NAME);
|
||||
UplinkMsg updatedUplinkMsg = getUplinkMsg(uuid, calculatedField, UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE);
|
||||
|
||||
checkCalculatedFieldOnCloud(updatedUplinkMsg, uuid, calculatedField.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculatedFieldToCloudWithNameThatAlreadyExistsOnCloud() throws Exception {
|
||||
Device savedDevice = saveDeviceOnCloudAndVerifyDeliveryToEdge();
|
||||
|
||||
// create calculatedField
|
||||
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration();
|
||||
CalculatedField calculatedField = createSimpleCalculatedField(savedDevice.getId(), config);
|
||||
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
CalculatedField savedCalculatedField = doPost("/api/calculatedField", calculatedField, CalculatedField.class);
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
UUID uuid = Uuids.timeBased();
|
||||
|
||||
UplinkMsg uplinkMsg = getUplinkMsg(uuid, calculatedField, UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE);
|
||||
|
||||
edgeImitator.expectResponsesAmount(1);
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
|
||||
edgeImitator.sendUplinkMsg(uplinkMsg);
|
||||
|
||||
Assert.assertTrue(edgeImitator.waitForResponses());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
Optional<CalculatedFieldUpdateMsg> calculatedFieldUpdateMsgOpt = edgeImitator.findMessageByType(CalculatedFieldUpdateMsg.class);
|
||||
Assert.assertTrue(calculatedFieldUpdateMsgOpt.isPresent());
|
||||
CalculatedFieldUpdateMsg latestCalculatedFieldUpdateMsg = calculatedFieldUpdateMsgOpt.get();
|
||||
CalculatedField calculatedFieldFromMsg = JacksonUtil.fromString(latestCalculatedFieldUpdateMsg.getEntity(), CalculatedField.class, true);
|
||||
Assert.assertNotNull(calculatedFieldFromMsg);
|
||||
Assert.assertNotEquals(DEFAULT_CF_NAME, calculatedFieldFromMsg.getName());
|
||||
|
||||
Assert.assertNotEquals(savedCalculatedField.getUuidId(), uuid);
|
||||
|
||||
CalculatedField calculatedFieldFromCloud = doGet("/api/calculatedField/" + uuid, CalculatedField.class);
|
||||
Assert.assertNotNull(calculatedFieldFromCloud);
|
||||
Assert.assertNotEquals(DEFAULT_CF_NAME, calculatedFieldFromCloud.getName());
|
||||
}
|
||||
|
||||
private CalculatedField createSimpleCalculatedField(EntityId entityId, SimpleCalculatedFieldConfiguration config) {
|
||||
CalculatedField calculatedField = new CalculatedField();
|
||||
calculatedField.setEntityId(entityId);
|
||||
calculatedField.setTenantId(tenantId);
|
||||
calculatedField.setType(CalculatedFieldType.SIMPLE);
|
||||
calculatedField.setName(DEFAULT_CF_NAME);
|
||||
calculatedField.setDebugSettings(DebugSettings.all());
|
||||
|
||||
Argument argument = new Argument();
|
||||
ReferencedEntityKey refEntityKey = new ReferencedEntityKey("temperature", ArgumentType.TS_LATEST, null);
|
||||
argument.setRefEntityKey(refEntityKey);
|
||||
argument.setDefaultValue("12"); // not used because real telemetry value in db is present
|
||||
config.setArguments(Map.of("T", argument));
|
||||
|
||||
config.setExpression("(T * 9/5) + 32");
|
||||
|
||||
Output output = new Output();
|
||||
output.setName("fahrenheitTemp");
|
||||
output.setType(OutputType.TIME_SERIES);
|
||||
output.setDecimalsByDefault(2);
|
||||
config.setOutput(output);
|
||||
|
||||
calculatedField.setConfiguration(config);
|
||||
|
||||
return calculatedField;
|
||||
}
|
||||
|
||||
private UplinkMsg getUplinkMsg(UUID uuid, CalculatedField calculatedField, UpdateMsgType updateMsgType) throws InvalidProtocolBufferException {
|
||||
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
|
||||
CalculatedFieldUpdateMsg.Builder calculatedFieldUpdateMsgBuilder = CalculatedFieldUpdateMsg.newBuilder();
|
||||
calculatedFieldUpdateMsgBuilder.setIdMSB(uuid.getMostSignificantBits());
|
||||
calculatedFieldUpdateMsgBuilder.setIdLSB(uuid.getLeastSignificantBits());
|
||||
calculatedFieldUpdateMsgBuilder.setEntity(JacksonUtil.toString(calculatedField));
|
||||
calculatedFieldUpdateMsgBuilder.setMsgType(updateMsgType);
|
||||
testAutoGeneratedCodeByProtobuf(calculatedFieldUpdateMsgBuilder);
|
||||
uplinkMsgBuilder.addCalculatedFieldUpdateMsg(calculatedFieldUpdateMsgBuilder.build());
|
||||
|
||||
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
|
||||
|
||||
return uplinkMsgBuilder.build();
|
||||
}
|
||||
|
||||
private void checkCalculatedFieldOnCloud(UplinkMsg uplinkMsg, UUID uuid, String resourceTitle) throws Exception {
|
||||
edgeImitator.expectResponsesAmount(1);
|
||||
edgeImitator.sendUplinkMsg(uplinkMsg);
|
||||
|
||||
Assert.assertTrue(edgeImitator.waitForResponses());
|
||||
|
||||
UplinkResponseMsg latestResponseMsg = edgeImitator.getLatestResponseMsg();
|
||||
Assert.assertTrue(latestResponseMsg.getSuccess());
|
||||
|
||||
CalculatedField calculatedField = doGet("/api/calculatedField/" + uuid, CalculatedField.class);
|
||||
Assert.assertNotNull(calculatedField);
|
||||
Assert.assertEquals(resourceTitle, calculatedField.getName());
|
||||
}
|
||||
|
||||
}
|
||||
@ -33,6 +33,7 @@ import org.thingsboard.server.gen.edge.v1.AlarmCommentUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.AlarmUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.AssetUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.CalculatedFieldUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.CustomerUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.DashboardUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.DeviceCredentialsRequestMsg;
|
||||
@ -352,6 +353,11 @@ public class EdgeImitator {
|
||||
result.add(saveDownlinkMsg(notificationTargetUpdateMsg));
|
||||
}
|
||||
}
|
||||
if (downlinkMsg.getCalculatedFieldUpdateMsgCount() > 0) {
|
||||
for (CalculatedFieldUpdateMsg calculatedFieldUpdateMsg : downlinkMsg.getCalculatedFieldUpdateMsgList()) {
|
||||
result.add(saveDownlinkMsg(calculatedFieldUpdateMsg));
|
||||
}
|
||||
}
|
||||
if (downlinkMsg.hasEdgeConfiguration()) {
|
||||
result.add(saveDownlinkMsg(downlinkMsg.getEdgeConfiguration()));
|
||||
}
|
||||
|
||||
@ -57,18 +57,23 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements
|
||||
|
||||
@Override
|
||||
public CalculatedField save(CalculatedField calculatedField) {
|
||||
return doSave(calculatedField, true);
|
||||
CalculatedField oldCalculatedField = calculatedFieldDataValidator.validate(calculatedField, CalculatedField::getTenantId);
|
||||
|
||||
return doSave(calculatedField, oldCalculatedField);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalculatedField save(CalculatedField calculatedField, boolean doValidate) {
|
||||
return doSave(calculatedField, doValidate);
|
||||
CalculatedField oldCalculatedField = null;
|
||||
|
||||
if (doValidate) {
|
||||
oldCalculatedField = calculatedFieldDataValidator.validate(calculatedField, CalculatedField::getTenantId);
|
||||
}
|
||||
|
||||
private CalculatedField doSave(CalculatedField calculatedField, boolean doValidate) {
|
||||
if (doValidate) {
|
||||
calculatedFieldDataValidator.validate(calculatedField, CalculatedField::getTenantId);
|
||||
return doSave(calculatedField, oldCalculatedField);
|
||||
}
|
||||
|
||||
private CalculatedField doSave(CalculatedField calculatedField, CalculatedField oldCalculatedField) {
|
||||
try {
|
||||
TenantId tenantId = calculatedField.getTenantId();
|
||||
log.trace("Executing save calculated field, [{}]", calculatedField);
|
||||
@ -76,7 +81,7 @@ public class BaseCalculatedFieldService extends AbstractEntityService implements
|
||||
CalculatedField savedCalculatedField = calculatedFieldDao.save(tenantId, calculatedField);
|
||||
createOrUpdateCalculatedFieldLink(tenantId, savedCalculatedField);
|
||||
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(savedCalculatedField.getTenantId()).entityId(savedCalculatedField.getId())
|
||||
.entity(savedCalculatedField).oldEntity(calculatedField).created(calculatedField.getId() == null).build());
|
||||
.entity(savedCalculatedField).oldEntity(oldCalculatedField).created(calculatedField.getId() == null).build());
|
||||
return savedCalculatedField;
|
||||
} catch (Exception e) {
|
||||
checkConstraintViolation(e,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user