Browse Source

Merge remote-tracking branch 'upstream/develop/3.4' into bug/fix-active-flag-on-cloud

pull/6684/head
Volodymyr Babak 4 years ago
parent
commit
5a51028577
  1. 2
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  2. 4
      application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java
  3. 20
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java
  4. 3
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java
  5. 9
      application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java
  6. 259
      application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java
  7. 2
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  8. 133
      application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
  9. 55
      application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
  10. 20
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
  11. 5
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java
  12. 4
      msa/js-executor/queue/kafkaTemplate.js
  13. 6
      msa/tb/docker-cassandra/Dockerfile
  14. 6
      msa/tb/docker-postgres/Dockerfile
  15. 15
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java
  16. 11
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java
  17. 176
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java
  18. 1
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java
  19. 1
      transport/coap/src/main/resources/tb-coap-transport.yml
  20. 1
      transport/http/src/main/resources/tb-http-transport.yml
  21. 1
      transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml
  22. 1
      transport/mqtt/src/main/resources/tb-mqtt-transport.yml
  23. 1
      transport/snmp/src/main/resources/tb-snmp-transport.yml
  24. 15
      ui-ngx/src/app/core/api/widget-subscription.ts
  25. 3
      ui-ngx/src/app/modules/home/components/widget/legend-config.component.html
  26. 6
      ui-ngx/src/app/modules/home/components/widget/legend-config.component.ts
  27. 8
      ui-ngx/src/app/modules/home/components/widget/legend.component.html
  28. 3
      ui-ngx/src/app/modules/home/components/widget/legend.component.ts
  29. 5
      ui-ngx/src/app/shared/models/widget.models.ts
  30. 2
      ui-ngx/src/assets/locale/locale.constant-en_US.json

2
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -108,7 +108,7 @@ public class DeviceProfileController extends BaseController {
checkParameter(DEVICE_PROFILE_ID, strDeviceProfileId);
try {
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId));
return checkNotNull(deviceProfileService.findDeviceProfileInfoById(getTenantId(), deviceProfileId));
return new DeviceProfileInfo(checkDeviceProfileId(deviceProfileId, Operation.READ));
} catch (Exception e) {
throw handleException(e);
}

4
application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java

@ -350,6 +350,10 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS
return EdgeEventActionType.RELATION_ADD_OR_UPDATE;
case RELATION_DELETED:
return EdgeEventActionType.RELATION_DELETED;
case ASSIGNED_TO_CUSTOMER:
return EdgeEventActionType.ASSIGNED_TO_CUSTOMER;
case UNASSIGNED_FROM_CUSTOMER:
return EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER;
case ASSIGNED_TO_EDGE:
return EdgeEventActionType.ASSIGNED_TO_EDGE;
case UNASSIGNED_FROM_EDGE:

20
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java

@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.queue.util.TbCoreComponent;
@ -54,7 +55,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
public void ack(Alarm alarm, SecurityUser user) throws ThingsboardException {
try {
long ackTs = System.currentTimeMillis();
alarmService.ackAlarm(user.getTenantId(), alarm.getId(), ackTs).get();
alarmService.ackAlarm(alarm.getTenantId(), alarm.getId(), ackTs).get();
alarm.setAckTs(ackTs);
alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK);
notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_ACK, user);
@ -67,7 +68,7 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
public void clear(Alarm alarm, SecurityUser user) throws ThingsboardException {
try {
long clearTs = System.currentTimeMillis();
alarmService.clearAlarm(user.getTenantId(), alarm.getId(), null, clearTs).get();
alarmService.clearAlarm(alarm.getTenantId(), alarm.getId(), null, clearTs).get();
alarm.setClearTs(clearTs);
alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK);
notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_CLEAR, user);
@ -78,11 +79,20 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
@Override
public Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException {
return delete(alarm, user.getCustomerId(), user);
}
@Override
public Boolean delete(Alarm alarm, CustomerId customerId) throws ThingsboardException {
return delete(alarm, customerId, null);
}
private Boolean delete(Alarm alarm, CustomerId customerId, SecurityUser user) throws ThingsboardException {
try {
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(user.getTenantId(), alarm.getOriginator());
notificationEntityService.notifyDeleteAlarm(user.getTenantId(), alarm, alarm.getOriginator(), user.getCustomerId(),
List<EdgeId> relatedEdgeIds = findRelatedEdgeIds(alarm.getTenantId(), alarm.getOriginator());
notificationEntityService.notifyDeleteAlarm(alarm.getTenantId(), alarm, alarm.getOriginator(), customerId,
relatedEdgeIds, user, JacksonUtil.OBJECT_MAPPER.writeValueAsString(alarm));
return alarmService.deleteAlarm(user.getTenantId(), alarm.getId()).isSuccessful();
return alarmService.deleteAlarm(alarm.getTenantId(), alarm.getId()).isSuccessful();
} catch (Exception e) {
throw handleException(e);
}

3
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.service.entitiy.alarm;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.service.security.model.SecurityUser;
public interface TbAlarmService {
@ -28,4 +29,6 @@ public interface TbAlarmService {
void clear(Alarm alarm, SecurityUser user) throws ThingsboardException;
Boolean delete(Alarm alarm, SecurityUser user) throws ThingsboardException;
Boolean delete(Alarm alarm, CustomerId customerId) throws ThingsboardException;
}

9
application/src/main/java/org/thingsboard/server/service/entitiy/asset/DefaultTbAssetService.java

@ -23,7 +23,6 @@ import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
@ -35,6 +34,8 @@ import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.List;
import static org.thingsboard.server.service.entitiy.DefaultTbNotificationEntityService.edgeTypeByActionType;
@Service
@TbCoreComponent
@AllArgsConstructor
@ -47,7 +48,7 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
try {
Asset savedAsset = checkNotNull(assetService.saveAsset(asset));
vcService.autoCommit(user, savedAsset.getId());
notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedAsset.getId(), asset, savedAsset.getCustomerId(), actionType, user);
notificationEntityService.notifyCreateOrUpdateEntity(tenantId, savedAsset.getId(), savedAsset, savedAsset.getCustomerId(), actionType, user);
return savedAsset;
} catch (Exception e) {
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.ASSET), asset, null, actionType, user, e);
@ -80,7 +81,7 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
try {
Asset savedAsset = checkNotNull(assetService.assignAssetToCustomer(tenantId, assetId, customerId));
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, assetId, customerId, savedAsset,
actionType, EdgeEventActionType.ASSIGNED_TO_CUSTOMER, user, true, customerId.toString(), customer.getName());
actionType, edgeTypeByActionType(actionType), user, true, customerId.toString(), customer.getName());
return savedAsset;
} catch (Exception e) {
@ -99,7 +100,7 @@ public class DefaultTbAssetService extends AbstractTbEntityService implements Tb
CustomerId customerId = customer.getId();
notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, assetId, customerId, savedAsset,
actionType, EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER, user,
actionType, edgeTypeByActionType(actionType), user,
true, customerId.toString(), customer.getName());
return savedAsset;

259
application/src/test/java/org/thingsboard/server/controller/AbstractNotifyEntityTest.java

@ -16,6 +16,7 @@
package org.thingsboard.server.controller;
import lombok.extern.slf4j.Slf4j;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.thingsboard.server.cluster.TbClusterService;
@ -33,6 +34,7 @@ import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.Locale;
import java.util.Objects;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@ -50,73 +52,86 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
protected void testNotifyEntityAllOneTime(HasName entity, EntityId entityId, EntityId originatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) {
testSendNotificationMsgToEdgeServiceOneTime(entityId, tenantId, actionType);
testLogEntityActionOneTime(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
testPushMsgToRuleEngineOneTime(originatorId, tenantId);
int cntTime = 1;
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, cntTime);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyEntityDeleteOneTimeMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) {
testNotificationMsgToEdgeServiceNever(entityId);
testLogEntityActionOneTime(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
testPushMsgToRuleEngineOneTime(entityId, tenantId);
testBroadcastEntityStateChangeEventOneTime(entityId, tenantId);
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId, ActionType actionType) {
testSendNotificationMsgToEdgeServiceOneTime(entityId, tenantId, actionType);
protected void testNotifyEntityNeverMsgToEdgeServiceOneTime(HasName entity, EntityId entityId, TenantId tenantId,
ActionType actionType) {
testSendNotificationMsgToEdgeServiceTime(entityId, tenantId, actionType, 1);
testLogEntityActionNever(entityId, entity);
testPushMsgToRuleEngineNever(entityId);
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyEntityOneTimeMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) {
TenantId tenantId, CustomerId customerId, UserId userId,
String userName, ActionType actionType, Object... additionalInfo) {
int cntTime = 1;
testNotificationMsgToEdgeServiceNever(entityId);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyManyEntityManyTimeMsgToEdgeServiceNever(HasName entity, HasName originator,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime, Object... additionalInfo) {
EntityId entityId = createEntityId_NULL_UUID(entity);
EntityId originatorId = createEntityId_NULL_UUID(originator);
testNotificationMsgToEdgeServiceNever(entityId);
testLogEntityActionOneTime(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
testPushMsgToRuleEngineOneTime(originatorId, tenantId);
ArgumentMatcher<HasName> matcherEntityClassEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.getClass().equals(originatorId.getClass());
testLogEntityActionAdditionalInfo(matcherEntityClassEquals, matcherOriginatorId, tenantId, customerId, userId, userName, actionType, cntTime,
additionalInfo);
testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyEntityBroadcastEntityStateChangeEventOneTimeMsgToEdgeServiceNever(HasName entity, EntityId entityId, EntityId originatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, Object... additionalInfo) {
int cntTime = 1;
testNotificationMsgToEdgeServiceNever(entityId);
testLogEntityActionOneTime(entity, originatorId, tenantId, customerId, userId, userName, actionType, additionalInfo);
testPushMsgToRuleEngineOneTime(originatorId, tenantId);
testBroadcastEntityStateChangeEventOneTime(entityId, tenantId);
testLogEntityAction(entity, originatorId, tenantId, customerId, userId, userName, actionType, cntTime, additionalInfo);
testPushMsgToRuleEngineTime(originatorId, tenantId, cntTime);
testBroadcastEntityStateChangeEventTime(entityId, tenantId, cntTime);
Mockito.reset(tbClusterService, auditLogService);
}
protected void testNotifyEntityError(HasName entity, TenantId tenantId,
UserId userId, String userName, ActionType actionType, Exception exp,
Object... additionalInfo) {
protected void testNotifyEntityEqualsOneTimeError(HasName entity, TenantId tenantId,
UserId userId, String userName, ActionType actionType, Exception exp,
Object... additionalInfo) {
CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID);
EntityId entity_NULL_UUID = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(entity.getClass().toString()
.substring(entity.getClass().toString().lastIndexOf(".") + 1).toUpperCase(Locale.ENGLISH)),
ModelConstants.NULL_UUID);
testNotificationMsgToEdgeServiceNever(entity_NULL_UUID);
if (additionalInfo.length > 0) {
Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customer_NULL_UUID), Mockito.eq(userId), Mockito.eq(userName),
Mockito.eq(entity_NULL_UUID), Mockito.any(entity.getClass()), Mockito.eq(actionType),
Mockito.argThat(argument ->
argument.getMessage().equals(exp.getMessage())), Mockito.eq(additionalInfo));
} else {
Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customer_NULL_UUID), Mockito.eq(userId), Mockito.eq(userName),
Mockito.eq(entity_NULL_UUID), Mockito.any(entity.getClass()), Mockito.eq(actionType),
Mockito.argThat(argument ->
argument.getMessage().equals(exp.getMessage())));
}
testPushMsgToRuleEngineNever(entity_NULL_UUID);
Mockito.reset(tbClusterService, auditLogService);
EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity);
testNotificationMsgToEdgeServiceNever(entity_originator_NULL_UUID);
ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.getClass().equals(entity.getClass());
ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage())
& argument.getClass().equals(exp.getClass());
testLogEntityActionErrorAdditionalInfo(matcherEntityEquals, entity_originator_NULL_UUID, tenantId, customer_NULL_UUID, userId,
userName, actionType, 1, matcherError, additionalInfo);
testPushMsgToRuleEngineNever(entity_originator_NULL_UUID);
}
protected void testNotifyEntityIsNullOneTimeError(HasName entity, TenantId tenantId,
UserId userId, String userName, ActionType actionType, Exception exp,
Object... additionalInfo) {
CustomerId customer_NULL_UUID = (CustomerId) EntityIdFactory.getByTypeAndUuid(EntityType.CUSTOMER, ModelConstants.NULL_UUID);
EntityId entity_originator_NULL_UUID = createEntityId_NULL_UUID(entity);
testNotificationMsgToEdgeServiceNever(entity_originator_NULL_UUID);
ArgumentMatcher<HasName> matcherEntityIsNull = Objects::isNull;
ArgumentMatcher<Exception> matcherError = argument -> argument.getMessage().contains(exp.getMessage()) &
argument.getClass().equals(exp.getClass());
testLogEntityActionErrorAdditionalInfo(matcherEntityIsNull, entity_originator_NULL_UUID, tenantId, customer_NULL_UUID,
userId, userName, actionType, 1, matcherError, additionalInfo);
testPushMsgToRuleEngineNever(entity_originator_NULL_UUID);
}
protected void testNotifyEntityNever(EntityId entityId, HasName entity) {
entityId = entityId == null ? createEntityId_NULL_UUID(entity) : entityId;
testNotificationMsgToEdgeServiceNever(entityId);
testLogEntityActionNever(entityId, entity);
testPushMsgToRuleEngineNever(entityId);
@ -139,36 +154,144 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
Mockito.any(entityId.getClass()), Mockito.any(), Mockito.any());
}
private void testLogEntityActionOneTime(HasName entity, EntityId originatorId, TenantId tenantId, CustomerId customerId,
UserId userId, String userName, ActionType actionType, Object... additionalInfo) {
if (additionalInfo.length == 0) {
Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId),
Mockito.eq(entity), Mockito.eq(actionType), Mockito.isNull());
} else {
String additionalInfoStr = extractParameter(String.class, 0, additionalInfo);
Mockito.verify(auditLogService, times(1)).logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId), Mockito.eq(userId), Mockito.eq(userName), Mockito.eq(originatorId),
Mockito.eq(entity), Mockito.eq(actionType), Mockito.isNull(), Mockito.eq(additionalInfoStr));
}
private void testPushMsgToRuleEngineTime(EntityId originatorId, TenantId tenantId, int cntTime) {
ArgumentMatcher<EntityId> matcherOriginatorId = cntTime == 1 ? argument -> argument.equals(originatorId) :
argument -> argument.getClass().equals(originatorId.getClass());
Mockito.verify(tbClusterService, times(cntTime)).pushMsgToRuleEngine(Mockito.eq(tenantId),
Mockito.argThat(matcherOriginatorId), Mockito.any(TbMsg.class), Mockito.isNull());
}
private void testPushMsgToRuleEngineOneTime(EntityId originatorId, TenantId tenantId) {
Mockito.verify(tbClusterService, times(1)).pushMsgToRuleEngine(Mockito.eq(tenantId),
Mockito.eq(originatorId), Mockito.any(TbMsg.class), Mockito.isNull());
}
private void testSendNotificationMsgToEdgeServiceOneTime(EntityId entityId, TenantId tenantId, ActionType actionType) {
Mockito.verify(tbClusterService, times(1)).sendNotificationMsgToEdge(Mockito.eq(tenantId),
Mockito.isNull(), Mockito.eq(entityId), Mockito.isNull(), Mockito.isNull(),
private void testSendNotificationMsgToEdgeServiceTime(EntityId entityId, TenantId tenantId, ActionType actionType, int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).sendNotificationMsgToEdge(Mockito.eq(tenantId),
Mockito.any(), Mockito.eq(entityId), Mockito.any(), Mockito.isNull(),
Mockito.eq(edgeTypeByActionType(actionType)));
}
private void testBroadcastEntityStateChangeEventOneTime(EntityId entityId, TenantId tenantId) {
Mockito.verify(tbClusterService, times(1)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId),
private void testBroadcastEntityStateChangeEventTime(EntityId entityId, TenantId tenantId, int cntTime) {
Mockito.verify(tbClusterService, times(cntTime)).broadcastEntityStateChangeEvent(Mockito.eq(tenantId),
Mockito.any(entityId.getClass()), Mockito.any(ComponentLifecycleEvent.class));
}
private void testLogEntityAction(HasName entity, EntityId originatorId, TenantId tenantId,
CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime, Object... additionalInfo) {
ArgumentMatcher<HasName> matcherEntityEquals = argument -> argument.equals(entity);
ArgumentMatcher<EntityId> matcherOriginatorId = argument -> argument.equals(originatorId);
testLogEntityActionAdditionalInfo(matcherEntityEquals, matcherOriginatorId, tenantId, customerId, userId, userName,
actionType, cntTime, additionalInfo);
}
private void testLogEntityActionAdditionalInfo(ArgumentMatcher<HasName> matcherEntity, ArgumentMatcher<EntityId> matcherOriginatorId,
TenantId tenantId, CustomerId customerId, UserId userId, String userName,
ActionType actionType, int cntTime, Object... additionalInfo) {
switch (additionalInfo.length) {
case 1:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
Mockito.eq(actionType),
Mockito.isNull(),
Mockito.eq(extractParameter(String.class, 0, additionalInfo)));
break;
case 2:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
Mockito.eq(actionType),
Mockito.isNull(),
Mockito.eq(extractParameter(String.class, 0, additionalInfo)),
Mockito.eq(extractParameter(String.class, 1, additionalInfo)));
break;
case 3:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
Mockito.eq(actionType),
Mockito.isNull(),
Mockito.eq(extractParameter(String.class, 0, additionalInfo)),
Mockito.eq(extractParameter(String.class, 1, additionalInfo)),
Mockito.eq(extractParameter(String.class, 2, additionalInfo)));
break;
default:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.eq(userName),
Mockito.argThat(matcherOriginatorId),
Mockito.argThat(matcherEntity),
Mockito.eq(actionType),
Mockito.isNull());
}
}
private void testLogEntityActionErrorAdditionalInfo(ArgumentMatcher<HasName> matcherEntity, EntityId originatorId, TenantId tenantId,
CustomerId customerId, UserId userId, String userName, ActionType actionType,
int cntTime, ArgumentMatcher<Exception> matcherError, Object... additionalInfo) {
switch (additionalInfo.length) {
case 1:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.eq(userName),
Mockito.eq(originatorId),
Mockito.argThat(matcherEntity),
Mockito.eq(actionType),
Mockito.argThat(matcherError),
Mockito.eq(extractParameter(String.class, 0, additionalInfo)));
break;
case 2:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.eq(userName),
Mockito.eq(originatorId),
Mockito.argThat(matcherEntity),
Mockito.eq(actionType),
Mockito.argThat(matcherError),
Mockito.eq(extractParameter(String.class, 0, additionalInfo)),
Mockito.eq(extractParameter(String.class, 1, additionalInfo)));
case 3:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.eq(userName),
Mockito.eq(originatorId),
Mockito.argThat(matcherEntity),
Mockito.eq(actionType),
Mockito.argThat(matcherError),
Mockito.eq(extractParameter(String.class, 0, additionalInfo)),
Mockito.eq(extractParameter(String.class, 1, additionalInfo)),
Mockito.eq(extractParameter(String.class, 3, additionalInfo)));
break;
default:
Mockito.verify(auditLogService, times(cntTime))
.logEntityAction(Mockito.eq(tenantId),
Mockito.eq(customerId),
Mockito.eq(userId),
Mockito.eq(userName),
Mockito.eq(originatorId),
Mockito.argThat(matcherEntity),
Mockito.eq(actionType),
Mockito.argThat(matcherError));
}
}
private <T> T extractParameter(Class<T> clazz, int index, Object... additionalInfo) {
T result = null;
if (additionalInfo != null && additionalInfo.length > index) {
@ -179,4 +302,10 @@ public abstract class AbstractNotifyEntityTest extends AbstractWebTest {
}
return result;
}
private EntityId createEntityId_NULL_UUID(HasName entity) {
return EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(entity.getClass().toString()
.substring(entity.getClass().toString().lastIndexOf(".") + 1).toUpperCase(Locale.ENGLISH)),
ModelConstants.NULL_UUID);
}
}

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

@ -237,6 +237,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
loginSysAdmin();
doDelete("/api/tenant/" + tenantId.getId().toString())
.andExpect(status().isOk());
deleteDifferentTenant();
verifyNoTenantsLeft();
@ -334,6 +335,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
loginSysAdmin();
doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
.andExpect(status().isOk());
savedDifferentTenant = null;
}
}

133
application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java

@ -22,17 +22,20 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EntitySubtype;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.service.stats.DefaultRuleEngineStatisticsService;
@ -83,8 +86,14 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Asset asset = new Asset();
asset.setName("My asset");
asset.setType("default");
Mockito.reset(tbClusterService, auditLogService);
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedAsset, savedAsset.getId(), savedAsset.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED);
Assert.assertNotNull(savedAsset);
Assert.assertNotNull(savedAsset.getId());
Assert.assertTrue(savedAsset.getCreatedTime() > 0);
@ -93,9 +102,14 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Assert.assertEquals(NULL_UUID, savedAsset.getCustomerId().getId());
Assert.assertEquals(asset.getName(), savedAsset.getName());
Mockito.reset(tbClusterService, auditLogService);
savedAsset.setName("My new asset");
doPost("/api/asset", savedAsset, Asset.class);
testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.UPDATED);
Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(foundAsset.getName(), savedAsset.getName());
}
@ -105,13 +119,33 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Asset asset = new Asset();
asset.setName(RandomStringUtils.randomAlphabetic(300));
asset.setType("default");
doPost("/api/asset", asset).andExpect(statusReason(containsString("length of name must be equal or less than 255")));
Mockito.reset(tbClusterService, auditLogService);
String msgError = "length of name must be equal or less than 255";
doPost("/api/asset", asset).andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
Mockito.reset(tbClusterService, auditLogService);
asset.setName("Normal name");
asset.setType(RandomStringUtils.randomAlphabetic(300));
doPost("/api/asset", asset).andExpect(statusReason(containsString("length of type must be equal or less than 255")));
msgError = "length of type must be equal or less than 255";
doPost("/api/asset", asset).andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
Mockito.reset(tbClusterService, auditLogService);
asset.setType("default");
asset.setLabel(RandomStringUtils.randomAlphabetic(300));
doPost("/api/asset", asset).andExpect(statusReason(containsString("length of label must be equal or less than 255")));
msgError = "length of label must be equal or less than 255";
doPost("/api/asset", asset).andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
}
@Test
@ -122,7 +156,13 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
loginDifferentTenant();
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/asset", savedAsset, Asset.class, status().isForbidden());
testNotifyEntityNever(savedAsset.getId(), savedAsset);
deleteDifferentTenant();
}
@ -140,12 +180,21 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
@Test
public void testFindAssetTypesByTenantId() throws Exception {
List<Asset> assets = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Mockito.reset(tbClusterService, auditLogService);
int cntTime = 3;
for (int i = 0; i < cntTime; i++) {
Asset asset = new Asset();
asset.setName("My asset B" + i);
asset.setType("typeB");
assets.add(doPost("/api/asset", asset, Asset.class));
}
testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Asset(), new Asset(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, cntTime);
for (int i = 0; i < 7; i++) {
Asset asset = new Asset();
asset.setName("My asset C" + i);
@ -176,9 +225,15 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/asset/" + savedAsset.getId().getId().toString())
.andExpect(status().isOk());
testNotifyEntityOneTimeMsgToEdgeServiceNever(savedAsset, savedAsset.getId(), savedAsset.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.DELETED, savedAsset.getId().getId().toString());
doGet("/api/asset/" + savedAsset.getId().getId().toString())
.andExpect(status().isNotFound());
}
@ -202,9 +257,16 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
view.setType("default");
EntityView savedView = doPost("/api/entityView", view, EntityView.class);
Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/asset/" + savedAsset1.getId().getId().toString())
.andExpect(status().isBadRequest());
String msgError = "Can't delete asset that has entity views";
testNotifyEntityIsNullOneTimeError(savedAsset1, savedTenant.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.DELETED, new DataValidationException(msgError), savedAsset1.getId().getId().toString());
savedView.setEntityId(savedAsset2.getId());
doPost("/api/entityView", savedView, EntityView.class);
@ -220,18 +282,32 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
public void testSaveAssetWithEmptyType() throws Exception {
Asset asset = new Asset();
asset.setName("My asset");
Mockito.reset(tbClusterService, auditLogService);
String msgError = "Asset type should be specified";
doPost("/api/asset", asset)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Asset type should be specified")));
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
}
@Test
public void testSaveAssetWithEmptyName() throws Exception {
Asset asset = new Asset();
asset.setType("default");
Mockito.reset(tbClusterService, auditLogService);
String msgError = "Asset name should be specified";
doPost("/api/asset", asset)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Asset name should be specified")));
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityEqualsOneTimeError(asset, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
}
@Test
@ -245,17 +321,29 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
Mockito.reset(tbClusterService, auditLogService);
Asset assignedAsset = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(savedCustomer.getId(), assignedAsset.getCustomerId());
testNotifyEntityAllOneTime(assignedAsset, assignedAsset.getId(), assignedAsset.getId(),
savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ASSIGNED_TO_CUSTOMER, savedCustomer.getId().toString(), savedCustomer.getTitle());
Asset foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(savedCustomer.getId(), foundAsset.getCustomerId());
Mockito.reset(tbClusterService, auditLogService);
Asset unassignedAsset =
doDelete("/api/customer/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(ModelConstants.NULL_UUID, unassignedAsset.getCustomerId().getId());
testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(),
savedTenant.getId(), savedCustomer.getId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UNASSIGNED_FROM_CUSTOMER, savedCustomer.getId().toString(), savedCustomer.getTitle());
foundAsset = doGet("/api/asset/" + savedAsset.getId().getId().toString(), Asset.class);
Assert.assertEquals(ModelConstants.NULL_UUID, foundAsset.getCustomerId().getId());
}
@ -267,9 +355,14 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
Mockito.reset(tbClusterService, auditLogService);
String msgError = "Asset name should be specified";
doPost("/api/customer/" + Uuids.timeBased().toString()
+ "/asset/" + savedAsset.getId().getId().toString())
.andExpect(status().isNotFound());
testNotifyEntityNever(asset.getId(), asset);
}
@Test
@ -301,10 +394,14 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString())
.andExpect(status().isForbidden());
testNotifyEntityNever(savedAsset.getId(), savedAsset);
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant2.getId().getId().toString())
@ -314,7 +411,11 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
@Test
public void testFindTenantAssets() throws Exception {
List<Asset> assets = new ArrayList<>();
for (int i = 0; i < 178; i++) {
int cntEntity = 178;
Mockito.reset(tbClusterService, auditLogService);
for (int i = 0; i < cntEntity; i++) {
Asset asset = new Asset();
asset.setName("Asset" + i);
asset.setType("default");
@ -332,6 +433,10 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
}
} while (pageData.hasNext());
testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Asset(), new Asset(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, cntEntity);
loadedAssets.removeIf(asset -> asset.getType().equals(DefaultRuleEngineStatisticsService.TB_SERVICE_QUEUE));
Collections.sort(assets, idComparator);
@ -731,17 +836,31 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
asset.setType("default");
Asset savedAsset = doPost("/api/asset", asset, Asset.class);
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/edge/" + savedEdge.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ASSIGNED_TO_EDGE,
savedAsset.getId().getId().toString(), savedEdge.getId().getId().toString(), edge.getName());
PageData<Asset> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>() {}, new PageLink(100));
Assert.assertEquals(1, pageData.getData().size());
Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/edge/" + savedEdge.getId().getId().toString()
+ "/asset/" + savedAsset.getId().getId().toString(), Asset.class);
testNotifyEntityAllOneTime(savedAsset, savedAsset.getId(), savedAsset.getId(),
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.UNASSIGNED_FROM_EDGE, savedAsset.getId().getId().toString(), savedEdge.getId().getId().toString(), savedEdge.getName());
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/assets?",
new TypeReference<PageData<Asset>>() {}, new PageLink(100));

55
application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java

@ -122,7 +122,6 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
@Test
public void testSaveCustomerWithViolationOfValidation() throws Exception {
Customer customer = new Customer();
String validationError = "Validation error: ";
customer.setTitle(RandomStringUtils.randomAlphabetic(300));
Mockito.reset(tbClusterService, auditLogService);
@ -130,49 +129,54 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
String msgError = "length of title must be equal or less than 255";
doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError)));
testNotifyEntityError(customer, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError));
customer.setTenantId(savedTenant.getId());
testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
Mockito.reset(tbClusterService, auditLogService);
customer.setTitle("Normal title");
customer.setCity(RandomStringUtils.randomAlphabetic(300));
msgError = "length of city must be equal or less than 255";
doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError)));
testNotifyEntityError(customer, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError));
testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
Mockito.reset(tbClusterService, auditLogService);
customer.setCity("Normal city");
customer.setCountry(RandomStringUtils.randomAlphabetic(300));
msgError = "length of country must be equal or less than 255";
doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError)));
testNotifyEntityError(customer, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError));
testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
Mockito.reset(tbClusterService, auditLogService);
customer.setCountry("Ukraine");
customer.setPhone(RandomStringUtils.randomAlphabetic(300));
msgError = "length of phone must be equal or less than 255";
doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError)));
testNotifyEntityError(customer, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError));
testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
Mockito.reset(tbClusterService, auditLogService);
customer.setPhone("+3892555554512");
customer.setState(RandomStringUtils.randomAlphabetic(300));
msgError = "length of state must be equal or less than 255";
doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError)));
testNotifyEntityError(customer, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError));
testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
Mockito.reset(tbClusterService, auditLogService);
customer.setState("Normal state");
customer.setZip(RandomStringUtils.randomAlphabetic(300));
msgError = "length of zip or postal code must be equal or less than 255";
doPost("/api/customer", customer).andExpect(statusReason(containsString(msgError)));
testNotifyEntityError(customer, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(validationError + msgError));
testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
}
@Test
@ -211,8 +215,6 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Assert.assertNotNull(foundCustomer);
Assert.assertEquals(savedCustomer, foundCustomer);
Mockito.reset(tbClusterService, auditLogService);
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
.andExpect(status().isOk());
}
@ -247,8 +249,8 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityError(customer, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError + "!"));
testNotifyEntityEqualsOneTimeError(customer,savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
}
@Test
@ -264,16 +266,20 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
testNotifyEntityError(customer, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError + "!"));
testNotifyEntityEqualsOneTimeError(customer, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError));
}
@Test
public void testFindCustomers() throws Exception {
TenantId tenantId = savedTenant.getId();
List<ListenableFuture<Customer>> futures = new ArrayList<>(135);
for (int i = 0; i < 135; i++) {
int cntEntity = 135;
Mockito.reset(tbClusterService, auditLogService);
List<ListenableFuture<Customer>> futures = new ArrayList<>(cntEntity);
for (int i = 0; i < cntEntity; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
customer.setTitle("Customer" + i);
@ -282,6 +288,10 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
}
List<Customer> customers = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
testNotifyManyEntityManyTimeMsgToEdgeServiceNever(new Customer(), new Customer(),
tenantId, tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(),
ActionType.ADDED, cntEntity);
List<Customer> loadedCustomers = new ArrayList<>(135);
PageLink pageLink = new PageLink(23);
PageData<Customer> pageData = null;
@ -370,5 +380,4 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
}

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

@ -126,6 +126,16 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
Assert.assertEquals(savedDeviceProfile, foundDeviceProfile);
}
@Test
public void whenGetDeviceProfileById_thenPermissionsAreChecked() throws Exception {
DeviceProfile deviceProfile = createDeviceProfile("Device profile 1", null);
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
loginDifferentTenant();
doGet("/api/deviceProfile/" + deviceProfile.getId())
.andExpect(status().isForbidden());
}
@Test
public void testFindDeviceProfileInfoById() throws Exception {
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile");
@ -137,6 +147,16 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
Assert.assertEquals(savedDeviceProfile.getType(), foundDeviceProfileInfo.getType());
}
@Test
public void whenGetDeviceProfileInfoById_thenPermissionsAreChecked() throws Exception {
DeviceProfile deviceProfile = createDeviceProfile("Device profile 1", null);
deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
loginDifferentTenant();
doGet("/api/deviceProfileInfo/" + deviceProfile.getId())
.andExpect(status().isForbidden());
}
@Test
public void testFindDefaultDeviceProfileInfo() throws Exception {
DeviceProfileInfo foundDefaultDeviceProfileInfo = doGet("/api/deviceProfileInfo/default", DeviceProfileInfo.class);

5
common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfileInfo.java

@ -63,4 +63,9 @@ public class DeviceProfileInfo extends EntityInfo {
this.transportType = transportType;
}
public DeviceProfileInfo(DeviceProfile profile) {
this(profile.getId(), profile.getName(), profile.getImage(), profile.getDefaultDashboardId(),
profile.getType(), profile.getTransportType());
}
}

4
msa/js-executor/queue/kafkaTemplate.js

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {logLevel, Kafka, CompressionTypes} = require('kafkajs');
const {logLevel, Kafka, CompressionTypes, Partitioners} = require('kafkajs');
const config = require('config'),
JsInvokeMessageProcessor = require('../api/jsInvokeMessageProcessor'),
@ -159,7 +159,7 @@ async function sendMessagesAsBatch(isImmediately) {
}
consumer = kafkaClient.consumer({groupId: 'js-executor-group'});
producer = kafkaClient.producer();
producer = kafkaClient.producer({ createPartitioner: Partitioners.DefaultPartitioner });
/*
//producer event instrumentation to debug

6
msa/tb/docker-cassandra/Dockerfile

@ -44,15 +44,17 @@ ENV CASSANDRA_LOG=/var/log/cassandra
COPY logback.xml ${pkg.name}.conf start-db.sh stop-db.sh start-tb.sh upgrade-tb.sh install-tb.sh ${pkg.name}.deb /tmp/
RUN apt-get update \
&& apt-get install -y curl nmap procps gnupg2 \
&& apt-get install -y --no-install-recommends curl nmap procps gnupg2 \
&& echo "deb http://apt.postgresql.org/pub/repos/apt/ $(. /etc/os-release && echo -n $VERSION_CODENAME)-pgdg main" | tee --append /etc/apt/sources.list.d/pgdg.list > /dev/null \
&& curl -L https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \
&& echo 'deb http://downloads.apache.org/cassandra/debian 40x main' | tee --append /etc/apt/sources.list.d/cassandra.list > /dev/null \
&& curl -L https://downloads.apache.org/cassandra/KEYS | apt-key add - \
&& apt-get update \
&& apt-get install -y cassandra cassandra-tools postgresql-12 \
&& apt-get install -y --no-install-recommends cassandra cassandra-tools postgresql-${PG_MAJOR} \
&& rm -rf /var/lib/apt/lists/* \
&& update-rc.d cassandra disable \
&& update-rc.d postgresql disable \
&& apt-get purge -y --auto-remove \
&& sed -i.old '/ulimit/d' /etc/init.d/cassandra \
&& chmod a+x /tmp/*.sh \
&& mv /tmp/start-tb.sh /usr/bin \

6
msa/tb/docker-postgres/Dockerfile

@ -36,12 +36,14 @@ ENV PGLOG=/var/log/postgres
COPY logback.xml ${pkg.name}.conf start-db.sh stop-db.sh start-tb.sh upgrade-tb.sh install-tb.sh ${pkg.name}.deb /tmp/
RUN apt-get update \
&& apt-get install -y curl gnupg2 \
&& apt-get install -y --no-install-recommends curl gnupg2 \
&& echo "deb http://apt.postgresql.org/pub/repos/apt/ $(. /etc/os-release && echo -n $VERSION_CODENAME)-pgdg main" | tee --append /etc/apt/sources.list.d/pgdg.list > /dev/null \
&& curl -L https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \
&& apt-get update \
&& apt-get install -y postgresql-12 \
&& apt-get install -y --no-install-recommends postgresql-${PG_MAJOR} \
&& rm -rf /var/lib/apt/lists/* \
&& update-rc.d postgresql disable \
&& apt-get purge -y --auto-remove \
&& chmod a+x /tmp/*.sh \
&& mv /tmp/start-tb.sh /usr/bin \
&& mv /tmp/upgrade-tb.sh /usr/bin \

15
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCopyAttributesToEntityViewNode.java

@ -21,6 +21,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.CollectionsUtil;
import org.thingsboard.common.util.DonAsynchron;
import org.thingsboard.rule.engine.api.EmptyNodeConfiguration;
import org.thingsboard.rule.engine.api.RuleNode;
@ -32,6 +33,7 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.objects.AttributesEntityView;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.session.SessionMsgType;
@ -140,15 +142,20 @@ public class TbCopyAttributesToEntityViewNode implements TbNode {
}
private boolean attributeContainsInEntityView(String scope, String attrKey, EntityView entityView) {
AttributesEntityView attributesEntityView = entityView.getKeys().getAttributes();
List<String> keys = null;
switch (scope) {
case DataConstants.CLIENT_SCOPE:
return entityView.getKeys().getAttributes().getCs().contains(attrKey);
keys = attributesEntityView.getCs();
break;
case DataConstants.SERVER_SCOPE:
return entityView.getKeys().getAttributes().getSs().contains(attrKey);
keys = attributesEntityView.getSs();
break;
case DataConstants.SHARED_SCOPE:
return entityView.getKeys().getAttributes().getSh().contains(attrKey);
keys = attributesEntityView.getSh();
break;
}
return false;
return CollectionsUtil.contains(keys, attrKey);
}
@Override

11
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java

@ -197,7 +197,8 @@ class DeviceState {
private void processAlarmDeleteNotification(TbContext ctx, TbMsg msg) {
Alarm alarm = JacksonUtil.fromString(msg.getData(), Alarm.class);
alarmStates.values().removeIf(alarmState -> alarmState.getCurrentAlarm().getId().equals(alarm.getId()));
alarmStates.values().removeIf(alarmState -> alarmState.getCurrentAlarm() != null
&& alarmState.getCurrentAlarm().getId().equals(alarm.getId()));
ctx.tellSuccess(msg);
}
@ -219,11 +220,15 @@ class DeviceState {
}
if (!keys.isEmpty()) {
EntityKeyType keyType = getKeyTypeFromScope(scope);
keys.forEach(key -> latestValues.removeValue(new EntityKey(keyType, key)));
Set<AlarmConditionFilterKey> removedKeys = keys.stream().map(key -> new EntityKey(keyType, key))
.peek(latestValues::removeValue)
.map(DataSnapshot::toConditionKey).collect(Collectors.toSet());
SnapshotUpdate update = new SnapshotUpdate(AlarmConditionKeyType.ATTRIBUTE, removedKeys);
for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) {
AlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(),
a -> new AlarmState(this.deviceProfile, deviceId, alarm, getOrInitPersistedAlarmState(alarm), dynamicPredicateValueCtx));
stateChanged |= alarmState.process(ctx, msg, latestValues, null);
stateChanged |= alarmState.process(ctx, msg, latestValues, update);
}
}
ctx.tellSuccess(msg);

176
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/DeviceStateTest.java

@ -0,0 +1,176 @@
/**
* Copyright © 2016-2022 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.rule.engine.profile;
import com.google.common.util.concurrent.Futures;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.device.profile.AlarmCondition;
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilter;
import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey;
import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType;
import org.thingsboard.server.common.data.device.profile.AlarmRule;
import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.device.profile.SimpleAlarmConditionSpec;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
import org.thingsboard.server.common.data.query.EntityKeyValueType;
import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.session.SessionMsgType;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.device.DeviceService;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class DeviceStateTest {
private TbContext ctx;
@Before
public void beforeEach() {
ctx = mock(TbContext.class);
when(ctx.getDeviceService()).thenReturn(mock(DeviceService.class));
AttributesService attributesService = mock(AttributesService.class);
when(attributesService.find(any(), any(), any(), anyCollection())).thenReturn(Futures.immediateFuture(Collections.emptyList()));
when(ctx.getAttributesService()).thenReturn(attributesService);
RuleEngineAlarmService alarmService = mock(RuleEngineAlarmService.class);
when(alarmService.findLatestByOriginatorAndType(any(), any(), any())).thenReturn(Futures.immediateFuture(null));
when(alarmService.createOrUpdateAlarm(any())).thenAnswer(invocationOnMock -> {
Alarm alarm = invocationOnMock.getArgument(0);
alarm.setId(new AlarmId(UUID.randomUUID()));
return alarm;
});
when(ctx.getAlarmService()).thenReturn(alarmService);
when(ctx.newMsg(any(), any(), any(), any(), any(), any())).thenAnswer(invocationOnMock -> {
String data = invocationOnMock.getArgument(invocationOnMock.getArguments().length - 1);
return TbMsg.newMsg(null, null, new TbMsgMetaData(), data);
});
}
@Test
public void whenAttributeIsDeleted_thenUnneededAlarmRulesAreNotReevaluated() throws Exception {
DeviceProfileAlarm alarmConfig = createAlarmConfigWithBoolAttrCondition("enabled", false);
DeviceId deviceId = new DeviceId(UUID.randomUUID());
DeviceState deviceState = createDeviceState(deviceId, alarmConfig);
TbMsg attributeUpdateMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(),
deviceId, new TbMsgMetaData(), "{ \"enabled\": false }");
deviceState.process(ctx, attributeUpdateMsg);
ArgumentCaptor<TbMsg> resultMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created"));
Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class);
deviceState.process(ctx, TbMsg.newMsg(DataConstants.ALARM_CLEAR, deviceId, new TbMsgMetaData(), JacksonUtil.toString(alarm)));
reset(ctx);
String deletedAttributes = "{ \"attributes\": [ \"other\" ] }";
deviceState.process(ctx, TbMsg.newMsg(DataConstants.ATTRIBUTES_DELETED, deviceId, new TbMsgMetaData(), deletedAttributes));
verify(ctx, never()).enqueueForTellNext(any(), anyString());
}
@Test
public void whenDeletingClearedAlarm_thenNoError() throws Exception {
DeviceProfileAlarm alarmConfig = createAlarmConfigWithBoolAttrCondition("enabled", false);
DeviceId deviceId = new DeviceId(UUID.randomUUID());
DeviceState deviceState = createDeviceState(deviceId, alarmConfig);
TbMsg attributeUpdateMsg = TbMsg.newMsg(SessionMsgType.POST_ATTRIBUTES_REQUEST.name(),
deviceId, new TbMsgMetaData(), "{ \"enabled\": false }");
deviceState.process(ctx, attributeUpdateMsg);
ArgumentCaptor<TbMsg> resultMsgCaptor = ArgumentCaptor.forClass(TbMsg.class);
verify(ctx).enqueueForTellNext(resultMsgCaptor.capture(), eq("Alarm Created"));
Alarm alarm = JacksonUtil.fromString(resultMsgCaptor.getValue().getData(), Alarm.class);
deviceState.process(ctx, TbMsg.newMsg(DataConstants.ALARM_CLEAR, deviceId, new TbMsgMetaData(), JacksonUtil.toString(alarm)));
TbMsg alarmDeleteNotification = TbMsg.newMsg(DataConstants.ALARM_DELETE, deviceId, new TbMsgMetaData(), JacksonUtil.toString(alarm));
assertDoesNotThrow(() -> {
deviceState.process(ctx, alarmDeleteNotification);
});
}
private DeviceState createDeviceState(DeviceId deviceId, DeviceProfileAlarm... alarmConfigs) {
DeviceProfile deviceProfile = new DeviceProfile();
DeviceProfileData profileData = new DeviceProfileData();
profileData.setAlarms(List.of(alarmConfigs));
deviceProfile.setProfileData(profileData);
ProfileState profileState = new ProfileState(deviceProfile);
return new DeviceState(ctx, new TbDeviceProfileNodeConfiguration(),
deviceId, profileState, null);
}
private DeviceProfileAlarm createAlarmConfigWithBoolAttrCondition(String key, boolean value) {
AlarmConditionFilter condition = new AlarmConditionFilter();
condition.setKey(new AlarmConditionFilterKey(AlarmConditionKeyType.ATTRIBUTE, key));
condition.setValueType(EntityKeyValueType.BOOLEAN);
BooleanFilterPredicate predicate = new BooleanFilterPredicate();
predicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
predicate.setValue(new FilterPredicateValue<>(value));
condition.setPredicate(predicate);
DeviceProfileAlarm alarmConfig = new DeviceProfileAlarm();
alarmConfig.setId("MyAlarmID");
alarmConfig.setAlarmType("MyAlarm");
AlarmRule alarmRule = new AlarmRule();
AlarmCondition alarmCondition = new AlarmCondition();
alarmCondition.setSpec(new SimpleAlarmConditionSpec());
alarmCondition.setCondition(List.of(condition));
alarmRule.setCondition(alarmCondition);
alarmConfig.setCreateRules(new TreeMap<>(Map.of(AlarmSeverity.CRITICAL, alarmRule)));
return alarmConfig;
}
}

1
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/profile/TbDeviceProfileNodeTest.java

@ -49,7 +49,6 @@ import org.thingsboard.server.common.data.device.profile.RepeatingAlarmCondition
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;

1
transport/coap/src/main/resources/tb-coap-transport.yml

@ -151,6 +151,7 @@ queue:
bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
acks: "${TB_KAFKA_ACKS:all}"
retries: "${TB_KAFKA_RETRIES:1}"
compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip
batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
linger.ms: "${TB_KAFKA_LINGER_MS:1}"
max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}"

1
transport/http/src/main/resources/tb-http-transport.yml

@ -138,6 +138,7 @@ queue:
bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
acks: "${TB_KAFKA_ACKS:all}"
retries: "${TB_KAFKA_RETRIES:1}"
compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip
batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
linger.ms: "${TB_KAFKA_LINGER_MS:1}"
max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}"

1
transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml

@ -216,6 +216,7 @@ queue:
bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
acks: "${TB_KAFKA_ACKS:all}"
retries: "${TB_KAFKA_RETRIES:1}"
compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip
batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
linger.ms: "${TB_KAFKA_LINGER_MS:1}"
max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}"

1
transport/mqtt/src/main/resources/tb-mqtt-transport.yml

@ -168,6 +168,7 @@ queue:
bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
acks: "${TB_KAFKA_ACKS:all}"
retries: "${TB_KAFKA_RETRIES:1}"
compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip
batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
linger.ms: "${TB_KAFKA_LINGER_MS:1}"
max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}"

1
transport/snmp/src/main/resources/tb-snmp-transport.yml

@ -114,6 +114,7 @@ queue:
bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}"
acks: "${TB_KAFKA_ACKS:all}"
retries: "${TB_KAFKA_RETRIES:1}"
compression.type: "${TB_KAFKA_COMPRESSION_TYPE:none}" # none or gzip
batch.size: "${TB_KAFKA_BATCH_SIZE:16384}"
linger.ms: "${TB_KAFKA_LINGER_MS:1}"
max.request.size: "${TB_KAFKA_MAX_REQUEST_SIZE:1048576}"

15
ui-ngx/src/app/core/api/widget-subscription.ts

@ -285,7 +285,8 @@ export class WidgetSubscription implements IWidgetSubscription {
(this.legendConfig.showMin === true ||
this.legendConfig.showMax === true ||
this.legendConfig.showAvg === true ||
this.legendConfig.showTotal === true);
this.legendConfig.showTotal === true ||
this.legendConfig.showLatest === true);
this.initDataSubscription().subscribe(() => {
subscriptionSubject.next(this);
subscriptionSubject.complete();
@ -1295,6 +1296,7 @@ export class WidgetSubscription implements IWidgetSubscription {
max: null,
avg: null,
total: null,
latest: null,
hidden: false
};
this.legendData.data.push(legendKeyData);
@ -1526,6 +1528,9 @@ export class WidgetSubscription implements IWidgetSubscription {
if (this.legendConfig.showTotal) {
legendKeyData.total = this.ctx.widgetUtils.formatValue(calculateTotal(data), decimals, units);
}
if (this.legendConfig.showLatest) {
legendKeyData.latest = this.ctx.widgetUtils.formatValue(calculateLatest(data), decimals, units);
}
this.callbacks.legendDataUpdated(this, detectChanges !== false);
}
@ -1598,3 +1603,11 @@ function calculateTotal(data: DataSet): number {
return null;
}
}
function calculateLatest(data: DataSet): number {
if (data.length > 0) {
return Number(data[data.length - 1][1]);
} else {
return null;
}
}

3
ui-ngx/src/app/modules/home/components/widget/legend-config.component.html

@ -50,6 +50,9 @@
<mat-checkbox formControlName="showTotal" fxFlex="48">
{{ 'legend.show-total' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="showLatest" fxFlex="48">
{{ 'legend.show-latest' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="sortDataKeys" fxFlex="48">
{{ 'legend.sort-legend' | translate }}
</mat-checkbox>

6
ui-ngx/src/app/modules/home/components/widget/legend-config.component.ts

@ -66,7 +66,8 @@ export class LegendConfigComponent implements OnInit, OnDestroy, ControlValueAcc
showMin: [null, []],
showMax: [null, []],
showAvg: [null, []],
showTotal: [null, []]
showTotal: [null, []],
showLatest: [null, []]
});
this.legendSettingsFormDirectionChanges$ = this.legendConfigForm.get('direction').valueChanges
.subscribe((direction: LegendDirection) => {
@ -124,7 +125,8 @@ export class LegendConfigComponent implements OnInit, OnDestroy, ControlValueAcc
showMin: isDefined(legendConfig.showMin) ? legendConfig.showMin : false,
showMax: isDefined(legendConfig.showMax) ? legendConfig.showMax : false,
showAvg: isDefined(legendConfig.showAvg) ? legendConfig.showAvg : false,
showTotal: isDefined(legendConfig.showTotal) ? legendConfig.showTotal : false
showTotal: isDefined(legendConfig.showTotal) ? legendConfig.showTotal : false,
showLatest: isDefined(legendConfig.showLatest) ? legendConfig.showLatest : false
}, {emitEvent: false});
}
this.onDirectionChanged(legendConfig.direction);

8
ui-ngx/src/app/modules/home/components/widget/legend.component.html

@ -23,6 +23,7 @@
<th *ngIf="legendConfig.showMax === true">{{ 'legend.max' | translate }}</th>
<th *ngIf="legendConfig.showAvg === true">{{ 'legend.avg' | translate }}</th>
<th *ngIf="legendConfig.showTotal === true">{{ 'legend.total' | translate }}</th>
<th *ngIf="legendConfig.showLatest === true">{{ 'legend.latest' | translate }}</th>
</tr>
</thead>
<tbody>
@ -38,6 +39,7 @@
<td class="tb-legend-value" *ngIf="legendConfig.showMax === true">{{ legendData.data[legendKey.dataIndex].max }}</td>
<td class="tb-legend-value" *ngIf="legendConfig.showAvg === true">{{ legendData.data[legendKey.dataIndex].avg }}</td>
<td class="tb-legend-value" *ngIf="legendConfig.showTotal === true">{{ legendData.data[legendKey.dataIndex].total }}</td>
<td class="tb-legend-value" *ngIf="legendConfig.showLatest === true">{{ legendData.data[legendKey.dataIndex].latest }}</td>
</tr>
</ng-container>
<tr *ngIf="isRowDirection" class="tb-legend-keys" [ngClass]="{ 'tb-row-direction': !displayHeader }">
@ -75,5 +77,11 @@
{{ legendData.data[legendKey.dataIndex].total }}
</td>
</tr>
<tr class="tb-legend-keys" *ngIf="isRowDirection && legendConfig.showLatest === true">
<td class="tb-legend-type">{{ 'legend.latest' | translate }}</td>
<td class="tb-legend-value" *ngFor="let legendKey of legendKeys()">
{{ legendData.data[legendKey.dataIndex].latest }}
</td>
</tr>
</tbody>
</table>

3
ui-ngx/src/app/modules/home/components/widget/legend.component.ts

@ -43,7 +43,8 @@ export class LegendComponent implements OnInit {
this.displayHeader = this.legendConfig.showMin === true ||
this.legendConfig.showMax === true ||
this.legendConfig.showAvg === true ||
this.legendConfig.showTotal === true;
this.legendConfig.showTotal === true ||
this.legendConfig.showLatest === true;
this.isHorizontal = this.legendConfig.position === LegendPosition.bottom ||
this.legendConfig.position === LegendPosition.top;

5
ui-ngx/src/app/shared/models/widget.models.ts

@ -243,6 +243,7 @@ export interface LegendConfig {
showMax: boolean;
showAvg: boolean;
showTotal: boolean;
showLatest: boolean;
}
export function defaultLegendConfig(wType: widgetType): LegendConfig {
@ -253,7 +254,8 @@ export function defaultLegendConfig(wType: widgetType): LegendConfig {
showMin: false,
showMax: false,
showAvg: wType === widgetType.timeseries,
showTotal: false
showTotal: false,
showLatest: false
};
}
@ -362,6 +364,7 @@ export interface LegendKeyData {
max: string;
avg: string;
total: string;
latest: string;
hidden: boolean;
}

2
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -2501,11 +2501,13 @@
"show-min": "Show min value",
"show-avg": "Show average value",
"show-total": "Show total value",
"show-latest": "Show latest value",
"settings": "Legend settings",
"min": "min",
"max": "max",
"avg": "avg",
"total": "total",
"latest": "latest",
"comparison-time-ago": {
"previousInterval": "(previous interval)",
"customInterval": "(custom interval)",

Loading…
Cancel
Save