Browse Source

Added Noxss, Length validation for alarm comment, deleted ttl service for alarm comments, added comment creation for ack, clear, severity updated events, fixed tests.

pull/7762/head
dashevchenko 4 years ago
parent
commit
0eb51cb17d
  1. 6
      application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java
  2. 7
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  3. 6
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java
  4. 14
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmService.java
  5. 2
      application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java
  6. 1
      application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java
  7. 1
      application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
  8. 1
      application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java
  9. 18
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java
  10. 62
      application/src/main/java/org/thingsboard/server/service/ttl/AlarmCommentsCleanUpService.java
  11. 4
      application/src/main/resources/thingsboard.yml
  12. 3
      application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java
  13. 6
      application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java
  14. 36
      common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentOperationResult.java
  15. 7
      common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java
  16. 7
      common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmOperationResult.java
  17. 8
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java
  18. 22
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentType.java
  19. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java
  20. 44
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java
  21. 5
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  22. 7
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmCommentEntity.java
  23. 23
      dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java
  24. 15
      dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java
  25. 7
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java
  26. 2
      dao/src/main/resources/sql/schema-entities-idx.sql
  27. 22
      dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmCommentServiceTest.java
  28. 9
      dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java

6
application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java

@ -89,14 +89,14 @@ public class AlarmCommentController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}/comment/{commentId}", method = RequestMethod.DELETE) @RequestMapping(value = "/alarm/{alarmId}/comment/{commentId}", method = RequestMethod.DELETE)
@ResponseBody @ResponseBody
public Boolean deleteAlarmComment(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @ApiParam(value = ALARM_COMMENT_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_COMMENT_ID) String strCommentId) throws ThingsboardException { public void deleteAlarmComment(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_ID) String strAlarmId, @ApiParam(value = ALARM_COMMENT_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_COMMENT_ID) String strCommentId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId); checkParameter(ALARM_ID, strAlarmId);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId, Operation.DELETE); Alarm alarm = checkAlarmId(alarmId, Operation.DELETE);
AlarmCommentId alarmCommentId = new AlarmCommentId(toUUID(strCommentId)); AlarmCommentId alarmCommentId = new AlarmCommentId(toUUID(strCommentId));
AlarmComment alarmComment = checkAlarmCommentId(alarmCommentId); AlarmComment alarmComment = checkAlarmCommentId(alarmCommentId, alarmId);
return tbAlarmCommentService.deleteAlarmComment(alarm, alarmComment, getCurrentUser()); tbAlarmCommentService.deleteAlarmComment(alarm, alarmComment, getCurrentUser());
} }
@ApiOperation(value = "Get Alarm comments (getAlarmComments)", @ApiOperation(value = "Get Alarm comments (getAlarmComments)",

7
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -722,12 +722,15 @@ public abstract class BaseController {
} }
} }
AlarmComment checkAlarmCommentId(AlarmCommentId alarmCommentId) throws ThingsboardException { AlarmComment checkAlarmCommentId(AlarmCommentId alarmCommentId, AlarmId alarmId) throws ThingsboardException {
try { try {
validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId); validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId);
AlarmComment alarmComment = alarmCommentService.findAlarmCommentByIdAsync(getCurrentUser().getTenantId(), alarmCommentId).get(); AlarmComment alarmComment = alarmCommentService.findAlarmCommentByIdAsync(getCurrentUser().getTenantId(), alarmCommentId).get();
checkNotNull(alarmComment, "Alarm comment with id [" + alarmCommentId + "] is not found"); checkNotNull(alarmComment, "Alarm comment with id [" + alarmCommentId + "] is not found");
return alarmComment; if (!alarmId.equals(alarmComment.getAlarmId())) {
throw new ThingsboardException("Alarm id does not match with comment alarm id", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
return alarmComment;
} catch (Exception e) { } catch (Exception e) {
throw handleException(e, false); throw handleException(e, false);
} }

6
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java

@ -35,7 +35,7 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem
UserId userId = user.getId(); UserId userId = user.getId();
alarmComment.setUserId(userId); alarmComment.setUserId(userId);
try { try {
AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment).getAlarmComment()); AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment));
notificationEntityService.notifyCreateOrUpdateAlarmComment(alarm, savedAlarmComment, actionType, user); notificationEntityService.notifyCreateOrUpdateAlarmComment(alarm, savedAlarmComment, actionType, user);
return savedAlarmComment; return savedAlarmComment;
} catch (Exception e) { } catch (Exception e) {
@ -45,8 +45,8 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem
} }
@Override @Override
public Boolean deleteAlarmComment(Alarm alarm, AlarmComment alarmComment, User user) { public void deleteAlarmComment(Alarm alarm, AlarmComment alarmComment, User user) {
alarmCommentService.deleteAlarmComment(alarm.getTenantId(), alarmComment.getId());
notificationEntityService.notifyDeleteAlarmComment(alarm, alarmComment, user); notificationEntityService.notifyDeleteAlarmComment(alarm, alarmComment, user);
return alarmCommentService.deleteAlarmComment(alarm.getTenantId(), alarmComment.getId()).isSuccessful();
} }
} }

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

@ -24,6 +24,8 @@ import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -56,6 +58,12 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
long ackTs = System.currentTimeMillis(); long ackTs = System.currentTimeMillis();
ListenableFuture<Boolean> future = alarmSubscriptionService.ackAlarm(alarm.getTenantId(), alarm.getId(), ackTs); ListenableFuture<Boolean> future = alarmSubscriptionService.ackAlarm(alarm.getTenantId(), alarm.getId(), ackTs);
return Futures.transform(future, result -> { return Futures.transform(future, result -> {
AlarmComment alarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was acknowledged by user %s", user.getName())))
.build();
alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment);
alarm.setAckTs(ackTs); alarm.setAckTs(ackTs);
alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK); alarm.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK);
notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_ACK, user); notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_ACK, user);
@ -68,6 +76,12 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
long clearTs = System.currentTimeMillis(); long clearTs = System.currentTimeMillis();
ListenableFuture<Boolean> future = alarmSubscriptionService.clearAlarm(alarm.getTenantId(), alarm.getId(), null, clearTs); ListenableFuture<Boolean> future = alarmSubscriptionService.clearAlarm(alarm.getTenantId(), alarm.getId(), null, clearTs);
return Futures.transform(future, result -> { return Futures.transform(future, result -> {
AlarmComment alarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm was cleared by user %s", user.getName())))
.build();
alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment);
alarm.setClearTs(clearTs); alarm.setClearTs(clearTs);
alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK); alarm.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK);
notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_CLEAR, user); notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_CLEAR, user);

2
application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java

@ -24,5 +24,5 @@ import org.thingsboard.server.common.data.id.TenantId;
public interface TbAlarmCommentService { public interface TbAlarmCommentService {
AlarmComment saveAlarmComment(Alarm alarm, AlarmComment alarmComment, User user) throws ThingsboardException; AlarmComment saveAlarmComment(Alarm alarm, AlarmComment alarmComment, User user) throws ThingsboardException;
Boolean deleteAlarmComment(Alarm alarm, AlarmComment alarmComment, User user); void deleteAlarmComment(Alarm alarm, AlarmComment alarmComment, User user);
} }

1
application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java

@ -32,7 +32,6 @@ public class CustomerUserPermissions extends AbstractPermissions {
public CustomerUserPermissions() { public CustomerUserPermissions() {
super(); super();
put(Resource.ALARM, customerAlarmPermissionChecker); put(Resource.ALARM, customerAlarmPermissionChecker);
put(Resource.ALARM_COMMENT, customerAlarmPermissionChecker);
put(Resource.ASSET, customerEntityPermissionChecker); put(Resource.ASSET, customerEntityPermissionChecker);
put(Resource.DEVICE, customerEntityPermissionChecker); put(Resource.DEVICE, customerEntityPermissionChecker);
put(Resource.CUSTOMER, customerPermissionChecker); put(Resource.CUSTOMER, customerPermissionChecker);

1
application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java

@ -22,7 +22,6 @@ import java.util.Optional;
public enum Resource { public enum Resource {
ADMIN_SETTINGS(), ADMIN_SETTINGS(),
ALARM(EntityType.ALARM), ALARM(EntityType.ALARM),
ALARM_COMMENT(EntityType.ALARM_COMMENT),
DEVICE(EntityType.DEVICE), DEVICE(EntityType.DEVICE),
ASSET(EntityType.ASSET), ASSET(EntityType.ASSET),
CUSTOMER(EntityType.CUSTOMER), CUSTOMER(EntityType.CUSTOMER),

1
application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java

@ -30,7 +30,6 @@ public class TenantAdminPermissions extends AbstractPermissions {
super(); super();
put(Resource.ADMIN_SETTINGS, PermissionChecker.allowAllPermissionChecker); put(Resource.ADMIN_SETTINGS, PermissionChecker.allowAllPermissionChecker);
put(Resource.ALARM, tenantEntityPermissionChecker); put(Resource.ALARM, tenantEntityPermissionChecker);
put(Resource.ALARM_COMMENT, tenantEntityPermissionChecker);
put(Resource.ASSET, tenantEntityPermissionChecker); put(Resource.ASSET, tenantEntityPermissionChecker);
put(Resource.DEVICE, tenantEntityPermissionChecker); put(Resource.DEVICE, tenantEntityPermissionChecker);
put(Resource.CUSTOMER, tenantEntityPermissionChecker); put(Resource.CUSTOMER, tenantEntityPermissionChecker);

18
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultAlarmSubscriptionService.java

@ -23,8 +23,11 @@ import lombok.extern.slf4j.Slf4j;
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.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.ApiUsageRecordKey; import org.thingsboard.server.common.data.ApiUsageRecordKey;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmQuery;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
@ -41,6 +44,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.common.msg.queue.TbCallback;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.common.stats.TbApiUsageReportClient; import org.thingsboard.server.common.stats.TbApiUsageReportClient;
import org.thingsboard.server.dao.alarm.AlarmCommentService;
import org.thingsboard.server.dao.alarm.AlarmOperationResult; import org.thingsboard.server.dao.alarm.AlarmOperationResult;
import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
@ -61,6 +65,7 @@ import java.util.Optional;
public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService implements AlarmSubscriptionService { public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService implements AlarmSubscriptionService {
private final AlarmService alarmService; private final AlarmService alarmService;
private final AlarmCommentService alarmCommentService;
private final TbApiUsageReportClient apiUsageClient; private final TbApiUsageReportClient apiUsageClient;
private final TbApiUsageStateService apiUsageStateService; private final TbApiUsageStateService apiUsageStateService;
@ -68,11 +73,13 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
PartitionService partitionService, PartitionService partitionService,
AlarmService alarmService, AlarmService alarmService,
TbApiUsageReportClient apiUsageClient, TbApiUsageReportClient apiUsageClient,
TbApiUsageStateService apiUsageStateService) { TbApiUsageStateService apiUsageStateService,
AlarmCommentService alarmCommentService) {
super(clusterService, partitionService); super(clusterService, partitionService);
this.alarmService = alarmService; this.alarmService = alarmService;
this.apiUsageClient = apiUsageClient; this.apiUsageClient = apiUsageClient;
this.apiUsageStateService = apiUsageStateService; this.apiUsageStateService = apiUsageStateService;
this.alarmCommentService = alarmCommentService;
} }
@Autowired(required = false) @Autowired(required = false)
@ -90,6 +97,15 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm, apiUsageStateService.getApiUsageState(alarm.getTenantId()).isAlarmCreationEnabled()); AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm, apiUsageStateService.getApiUsageState(alarm.getTenantId()).isAlarmCreationEnabled());
if (result.isSuccessful()) { if (result.isSuccessful()) {
onAlarmUpdated(result); onAlarmUpdated(result);
AlarmSeverity oldSeverity = result.getOldSeverity();
if (oldSeverity != null && !oldSeverity.equals(result.getAlarm().getSeverity())) {
AlarmComment alarmComment = AlarmComment.builder()
.alarmId(alarm.getId())
.type(AlarmCommentType.SYSTEM)
.comment(JacksonUtil.newObjectNode().put("text", String.format("Alarm severity was updated from %s to %s", oldSeverity, result.getAlarm().getSeverity())))
.build();
alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment);
}
} }
if (result.isCreated()) { if (result.isCreated()) {
apiUsageClient.report(alarm.getTenantId(), null, ApiUsageRecordKey.CREATED_ALARMS_COUNT); apiUsageClient.report(alarm.getTenantId(), null, ApiUsageRecordKey.CREATED_ALARMS_COUNT);

62
application/src/main/java/org/thingsboard/server/service/ttl/AlarmCommentsCleanUpService.java

@ -1,62 +0,0 @@
/**
* 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.server.service.ttl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.server.dao.alarm.AlarmCommentDao;
import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository;
import org.thingsboard.server.queue.discovery.PartitionService;
import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_COLUMN_FAMILY_NAME;
import static org.thingsboard.server.dao.model.ModelConstants.AUDIT_LOG_COLUMN_FAMILY_NAME;
@Service
@ConditionalOnExpression("${sql.ttl.alarm_comments.enabled:true} && ${sql.ttl.alarm_comments.ttl:0} > 0")
@Slf4j
public class AlarmCommentsCleanUpService extends AbstractCleanUpService {
private final AlarmCommentDao alarmCommentDao;
private final SqlPartitioningRepository partitioningRepository;
@Value("${sql.ttl.alarm_comments.ttl:0}")
private long ttlInSec;
@Value("${sql.alarm_comments.partition_size:168}")
private int partitionSizeInHours;
public AlarmCommentsCleanUpService(PartitionService partitionService, AlarmCommentDao alarmCommentDao, SqlPartitioningRepository partitioningRepository) {
super(partitionService);
this.alarmCommentDao = alarmCommentDao;
this.partitioningRepository = partitioningRepository;
}
@Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.alarm_comments.checking_interval_ms})}",
fixedDelayString = "${sql.ttl.alarm_comments.checking_interval_ms}")
public void cleanUp() {
long commentsExpTime = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(ttlInSec);
if (isSystemTenantPartitionMine()) {
alarmCommentDao.cleanUpAlarmComments(commentsExpTime);
} else {
partitioningRepository.cleanupPartitionsCache(ALARM_COMMENT_COLUMN_FAMILY_NAME, commentsExpTime, TimeUnit.HOURS.toMillis(partitionSizeInHours));
}
}
}

4
application/src/main/resources/thingsboard.yml

@ -314,10 +314,6 @@ sql:
alarms: alarms:
checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours
removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches
alarms_comments:
enabled: "${SQL_TTL_ALARM_COMMENTS_ENABLED:true}"
ttl: "${SQL_TTL_ALARM_COMMENTS_SECS:0}" # Disabled by default. Accuracy of the cleanup depends on the sql.alarm_comments.partition_size
checking_interval_ms: "${SQL_TTL_ALARM_COMMENTS_CHECKING_INTERVAL_MS:86400000}" # Default value - 1 day
rpc: rpc:
enabled: "${SQL_TTL_RPC_ENABLED:true}" enabled: "${SQL_TTL_RPC_ENABLED:true}"
checking_interval: "${SQL_RPC_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours checking_interval: "${SQL_RPC_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours

3
application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java

@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentInfo; import org.thingsboard.server.common.data.alarm.AlarmCommentInfo;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.audit.ActionType;
@ -115,7 +116,7 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
Mockito.reset(tbClusterService, auditLogService); Mockito.reset(tbClusterService, auditLogService);
AlarmComment createdComment = createAlarmComment(alarm.getId()); AlarmComment createdComment = createAlarmComment(alarm.getId());
Assert.assertEquals("OTHER", createdComment.getType()); Assert.assertEquals(AlarmCommentType.OTHER, createdComment.getType());
testLogEntityAction(createdComment, createdComment.getId(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED, 1); testLogEntityAction(createdComment, createdComment.getId(), tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ADDED, 1);
testPushMsgToRuleEngineTime(createdComment.getId(), tenantId, createdComment, 1); testPushMsgToRuleEngineTime(createdComment.getId(), tenantId, createdComment, 1);

6
application/src/test/java/org/thingsboard/server/service/entitiy/alarmComment/DefaultTbAlarmCommentServiceTest.java

@ -31,7 +31,6 @@ import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.dao.alarm.AlarmCommentOperationResult;
import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.dao.alarm.AlarmCommentService;
import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.customer.CustomerService;
@ -44,6 +43,7 @@ import java.util.UUID;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -77,7 +77,7 @@ public class DefaultTbAlarmCommentServiceTest {
public void testSave() throws ThingsboardException { public void testSave() throws ThingsboardException {
var alarm = new Alarm(); var alarm = new Alarm();
var alarmComment = new AlarmComment(); var alarmComment = new AlarmComment();
when(alarmCommentService.createOrUpdateAlarmComment(Mockito.any(), eq(alarmComment))).thenReturn(new AlarmCommentOperationResult(alarmComment, true)); when(alarmCommentService.createOrUpdateAlarmComment(Mockito.any(), eq(alarmComment))).thenReturn(alarmComment);
service.saveAlarmComment(alarm, alarmComment, new User()); service.saveAlarmComment(alarm, alarmComment, new User());
verify(notificationEntityService, times(1)).notifyCreateOrUpdateAlarmComment(any(), any(), any(), any()); verify(notificationEntityService, times(1)).notifyCreateOrUpdateAlarmComment(any(), any(), any(), any());
@ -88,7 +88,7 @@ public class DefaultTbAlarmCommentServiceTest {
var alarmId = new AlarmId(UUID.randomUUID()); var alarmId = new AlarmId(UUID.randomUUID());
var alarmCommentId = new AlarmCommentId(UUID.randomUUID()); var alarmCommentId = new AlarmCommentId(UUID.randomUUID());
when(alarmCommentService.deleteAlarmComment(Mockito.any(), eq(alarmCommentId))).thenReturn(new AlarmCommentOperationResult(new AlarmComment(), true)); doNothing().when(alarmCommentService).deleteAlarmComment(Mockito.any(), eq(alarmCommentId));
service.deleteAlarmComment(new Alarm(alarmId), new AlarmComment(alarmCommentId), new User()); service.deleteAlarmComment(new Alarm(alarmId), new AlarmComment(alarmCommentId), new User());
verify(notificationEntityService, times(1)).notifyDeleteAlarmComment(any(), any(), any()); verify(notificationEntityService, times(1)).notifyDeleteAlarmComment(any(), any(), any());

36
common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentOperationResult.java

@ -1,36 +0,0 @@
/**
* 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.server.dao.alarm;
import lombok.Data;
import org.thingsboard.server.common.data.alarm.AlarmComment;
@Data
public class AlarmCommentOperationResult {
private final AlarmComment alarmComment;
private final boolean successful;
private final boolean created;
public AlarmCommentOperationResult(AlarmComment alarmComment, boolean successful) {
this(alarmComment, successful, false);
}
public AlarmCommentOperationResult(AlarmComment alarmComment, boolean successful, boolean created) {
this.alarmComment = alarmComment;
this.successful = successful;
this.created = created;
}
}

7
common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java

@ -23,11 +23,12 @@ import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.EntityDaoService;
public interface AlarmCommentService { public interface AlarmCommentService extends EntityDaoService {
AlarmCommentOperationResult createOrUpdateAlarmComment(TenantId tenantId, AlarmComment alarmComment); AlarmComment createOrUpdateAlarmComment(TenantId tenantId, AlarmComment alarmComment);
AlarmCommentOperationResult deleteAlarmComment(TenantId tenantId, AlarmCommentId alarmCommentId); void deleteAlarmComment(TenantId tenantId, AlarmCommentId alarmCommentId);
PageData<AlarmCommentInfo> findAlarmComments(TenantId tenantId, AlarmId alarmId, PageLink pageLink); PageData<AlarmCommentInfo> findAlarmComments(TenantId tenantId, AlarmId alarmId, PageLink pageLink);

7
common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmOperationResult.java

@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
import lombok.Data; import lombok.Data;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import java.util.Collections; import java.util.Collections;
@ -27,6 +28,7 @@ public class AlarmOperationResult {
private final Alarm alarm; private final Alarm alarm;
private final boolean successful; private final boolean successful;
private final boolean created; private final boolean created;
private final AlarmSeverity oldSeverity;
private final List<EntityId> propagatedEntitiesList; private final List<EntityId> propagatedEntitiesList;
public AlarmOperationResult(Alarm alarm, boolean successful) { public AlarmOperationResult(Alarm alarm, boolean successful) {
@ -34,13 +36,14 @@ public class AlarmOperationResult {
} }
public AlarmOperationResult(Alarm alarm, boolean successful, List<EntityId> propagatedEntitiesList) { public AlarmOperationResult(Alarm alarm, boolean successful, List<EntityId> propagatedEntitiesList) {
this(alarm, successful, false, propagatedEntitiesList); this(alarm, successful, false, null, propagatedEntitiesList);
} }
public AlarmOperationResult(Alarm alarm, boolean successful, boolean created, List<EntityId> propagatedEntitiesList) { public AlarmOperationResult(Alarm alarm, boolean successful, boolean created, AlarmSeverity oldSeverity, List<EntityId> propagatedEntitiesList) {
this.alarm = alarm; this.alarm = alarm;
this.successful = successful; this.successful = successful;
this.created = created; this.created = created;
this.oldSeverity = oldSeverity;
this.propagatedEntitiesList = propagatedEntitiesList; this.propagatedEntitiesList = propagatedEntitiesList;
} }
} }

8
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java

@ -39,11 +39,11 @@ public class AlarmComment extends BaseData<AlarmCommentId> implements HasName {
private EntityId alarmId; private EntityId alarmId;
@ApiModelProperty(position = 4, value = "JSON object with User id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @ApiModelProperty(position = 4, value = "JSON object with User id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private UserId userId; private UserId userId;
@ApiModelProperty(position = 5, value = "Defines origination of comment", example = "System/Other", accessMode = ApiModelProperty.AccessMode.READ_ONLY) @ApiModelProperty(position = 5, value = "Defines origination of comment. System type means comment was created by TB. OTHER type means comment was created by user.", example = "SYSTEM/OTHER", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@NoXss private AlarmCommentType type;
@Length(fieldName = "type")
private String type;
@ApiModelProperty(position = 6, value = "JSON object with text of comment.", dataType = "com.fasterxml.jackson.databind.JsonNode") @ApiModelProperty(position = 6, value = "JSON object with text of comment.", dataType = "com.fasterxml.jackson.databind.JsonNode")
@NoXss
@Length(fieldName = "comment", max = 10000)
private transient JsonNode comment; private transient JsonNode comment;
@ApiModelProperty(position = 1, value = "JSON object with the alarm comment Id. " + @ApiModelProperty(position = 1, value = "JSON object with the alarm comment Id. " +

22
common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentType.java

@ -0,0 +1,22 @@
/**
* 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.server.common.data.alarm;
public enum AlarmCommentType {
SYSTEM, OTHER;
}

2
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java

@ -38,6 +38,4 @@ public interface AlarmCommentDao extends Dao<AlarmComment> {
PageData<AlarmCommentInfo> findAlarmComments(TenantId tenantId, AlarmId id, PageLink pageLink); PageData<AlarmCommentInfo> findAlarmComments(TenantId tenantId, AlarmId id, PageLink pageLink);
ListenableFuture<AlarmComment> findAlarmCommentByIdAsync(TenantId tenantId, UUID key); ListenableFuture<AlarmComment> findAlarmCommentByIdAsync(TenantId tenantId, UUID key);
void cleanUpAlarmComments(long auditLogsExpTime);
} }

44
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java

@ -18,26 +18,25 @@ package org.thingsboard.server.dao.alarm;
import com.datastax.oss.driver.api.core.uuid.Uuids; import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentInfo; import org.thingsboard.server.common.data.alarm.AlarmCommentInfo;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.user.UserService;
import java.util.ArrayList; import java.util.Optional;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateId;
@ -49,15 +48,12 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
@Autowired @Autowired
private AlarmCommentDao alarmCommentDao; private AlarmCommentDao alarmCommentDao;
@Autowired
private UserService userService;
@Autowired @Autowired
private DataValidator<AlarmComment> alarmCommentDataValidator; private DataValidator<AlarmComment> alarmCommentDataValidator;
@Override @Override
public AlarmCommentOperationResult createOrUpdateAlarmComment(TenantId tenantId, AlarmComment alarmComment) { public AlarmComment createOrUpdateAlarmComment(TenantId tenantId, AlarmComment alarmComment) {
alarmCommentDataValidator.validate(alarmComment, tenantId); alarmCommentDataValidator.validate(alarmComment, c -> tenantId);
if (alarmComment.getId() == null) { if (alarmComment.getId() == null) {
return createAlarmComment(tenantId, alarmComment); return createAlarmComment(tenantId, alarmComment);
} else { } else {
@ -66,11 +62,9 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
} }
@Override @Override
public AlarmCommentOperationResult deleteAlarmComment(TenantId tenantId, AlarmCommentId alarmCommentId) { public void deleteAlarmComment(TenantId tenantId, AlarmCommentId alarmCommentId) {
log.debug("Deleting Alarm Comment with id: {}", alarmCommentId); log.debug("Deleting Alarm Comment with id: {}", alarmCommentId);
AlarmCommentOperationResult result = new AlarmCommentOperationResult(new AlarmComment(), true);
alarmCommentDao.deleteAlarmComment(tenantId, alarmCommentId); alarmCommentDao.deleteAlarmComment(tenantId, alarmCommentId);
return result;
} }
@Override @Override
@ -93,21 +87,20 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
return alarmCommentDao.findById(tenantId, alarmCommentId.getId()); return alarmCommentDao.findById(tenantId, alarmCommentId.getId());
} }
private AlarmCommentOperationResult createAlarmComment(TenantId tenantId, AlarmComment alarmComment) { private AlarmComment createAlarmComment(TenantId tenantId, AlarmComment alarmComment) {
log.debug("New Alarm comment : {}", alarmComment); log.debug("New Alarm comment : {}", alarmComment);
if (alarmComment.getType() == null) { if (alarmComment.getType() == null) {
alarmComment.setType("OTHER"); alarmComment.setType(AlarmCommentType.OTHER);
} }
if (alarmComment.getId() == null) { if (alarmComment.getId() == null) {
UUID uuid = Uuids.timeBased(); UUID uuid = Uuids.timeBased();
alarmComment.setId(new AlarmCommentId(uuid)); alarmComment.setId(new AlarmCommentId(uuid));
alarmComment.setCreatedTime(Uuids.unixTimestamp(uuid)); alarmComment.setCreatedTime(Uuids.unixTimestamp(uuid));
} }
AlarmComment saved = alarmCommentDao.createAlarmComment(tenantId, alarmComment); return alarmCommentDao.createAlarmComment(tenantId, alarmComment);
return new AlarmCommentOperationResult(saved, true, true);
} }
private AlarmCommentOperationResult updateAlarmComment(TenantId tenantId, AlarmComment newAlarmComment) { private AlarmComment updateAlarmComment(TenantId tenantId, AlarmComment newAlarmComment) {
log.debug("Update Alarm comment : {}", newAlarmComment); log.debug("Update Alarm comment : {}", newAlarmComment);
AlarmComment existing = alarmCommentDao.findAlarmCommentById(tenantId, newAlarmComment.getId().getId()); AlarmComment existing = alarmCommentDao.findAlarmCommentById(tenantId, newAlarmComment.getId().getId());
@ -119,9 +112,18 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
((ObjectNode) comment).put("editedOn", Uuids.unixTimestamp(uuid)); ((ObjectNode) comment).put("editedOn", Uuids.unixTimestamp(uuid));
existing.setComment(comment); existing.setComment(comment);
} }
AlarmComment result = alarmCommentDao.save(tenantId, existing); return alarmCommentDao.save(tenantId, existing);
return new AlarmCommentOperationResult(result, true, true);
} }
return null; return null;
} }
@Override
public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
return Optional.ofNullable(findAlarmCommentById(tenantId, new AlarmCommentId(entityId.getId())));
}
@Override
public EntityType getEntityType() {
return EntityType.ALARM_COMMENT;
}
} }

5
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -165,7 +165,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
log.debug("New Alarm : {}", alarm); log.debug("New Alarm : {}", alarm);
Alarm saved = alarmDao.save(alarm.getTenantId(), alarm); Alarm saved = alarmDao.save(alarm.getTenantId(), alarm);
List<EntityId> propagatedEntitiesList = createEntityAlarmRecords(saved); List<EntityId> propagatedEntitiesList = createEntityAlarmRecords(saved);
return new AlarmOperationResult(saved, true, true, propagatedEntitiesList); return new AlarmOperationResult(saved, true, true, null, propagatedEntitiesList);
} }
private List<EntityId> createEntityAlarmRecords(Alarm alarm) throws InterruptedException, ExecutionException { private List<EntityId> createEntityAlarmRecords(Alarm alarm) throws InterruptedException, ExecutionException {
@ -208,6 +208,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
boolean propagationEnabled = !oldAlarm.isPropagate() && newAlarm.isPropagate(); boolean propagationEnabled = !oldAlarm.isPropagate() && newAlarm.isPropagate();
boolean propagationToOwnerEnabled = !oldAlarm.isPropagateToOwner() && newAlarm.isPropagateToOwner(); boolean propagationToOwnerEnabled = !oldAlarm.isPropagateToOwner() && newAlarm.isPropagateToOwner();
boolean propagationToTenantEnabled = !oldAlarm.isPropagateToTenant() && newAlarm.isPropagateToTenant(); boolean propagationToTenantEnabled = !oldAlarm.isPropagateToTenant() && newAlarm.isPropagateToTenant();
AlarmSeverity oldAlarmSeverity = oldAlarm.getSeverity();
Alarm result = alarmDao.save(newAlarm.getTenantId(), merge(oldAlarm, newAlarm)); Alarm result = alarmDao.save(newAlarm.getTenantId(), merge(oldAlarm, newAlarm));
List<EntityId> propagatedEntitiesList; List<EntityId> propagatedEntitiesList;
if (propagationEnabled || propagationToOwnerEnabled || propagationToTenantEnabled) { if (propagationEnabled || propagationToOwnerEnabled || propagationToTenantEnabled) {
@ -220,7 +221,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} else { } else {
propagatedEntitiesList = new ArrayList<>(getPropagationEntityIds(result)); propagatedEntitiesList = new ArrayList<>(getPropagationEntityIds(result));
} }
return new AlarmOperationResult(result, true, propagatedEntitiesList); return new AlarmOperationResult(result, true, false, oldAlarmSeverity, propagatedEntitiesList);
} }
@Override @Override

7
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAlarmCommentEntity.java

@ -21,6 +21,7 @@ import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
@ -50,7 +51,7 @@ public abstract class AbstractAlarmCommentEntity<T extends AlarmComment> extends
private UUID userId; private UUID userId;
@Column(name = ALARM_COMMENT_TYPE) @Column(name = ALARM_COMMENT_TYPE)
private String type; private AlarmCommentType type;
@Type(type = "json") @Type(type = "json")
@Column(name = ALARM_COMMENT_COMMENT) @Column(name = ALARM_COMMENT_COMMENT)
@ -65,9 +66,7 @@ public abstract class AbstractAlarmCommentEntity<T extends AlarmComment> extends
this.setUuid(alarmComment.getUuidId()); this.setUuid(alarmComment.getUuidId());
} }
this.setCreatedTime(alarmComment.getCreatedTime()); this.setCreatedTime(alarmComment.getCreatedTime());
if (alarmComment.getAlarmId() != null) { this.alarmId = alarmComment.getAlarmId().getId();
this.alarmId = alarmComment.getAlarmId().getId();
}
if (alarmComment.getUserId() != null) { if (alarmComment.getUserId() != null) {
this.userId = alarmComment.getUserId().getId(); this.userId = alarmComment.getUserId().getId();
} }

23
dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java

@ -67,29 +67,6 @@ public abstract class DataValidator<D extends BaseData<?>> {
} }
} }
public D validate(D data, TenantId tenantId) {
try {
if (data == null) {
throw new DataValidationException("Data object can't be null!");
}
ConstraintValidator.validateFields(data);
validateDataImpl(tenantId, data);
D old;
if (data.getId() == null) {
validateCreate(tenantId, data);
old = null;
} else {
old = validateUpdate(tenantId, data);
}
return old;
} catch (DataValidationException e) {
log.error("{} object is invalid: [{}]", data == null ? "Data" : data.getClass().getSimpleName(), e.getMessage());
throw e;
}
}
protected void validateDataImpl(TenantId tenantId, D data) { protected void validateDataImpl(TenantId tenantId, D data) {
} }

15
dao/src/main/java/org/thingsboard/server/dao/service/StringLengthValidator.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.dao.service; package org.thingsboard.server.dao.service;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.Length;
@ -23,15 +24,21 @@ import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import javax.validation.ConstraintValidatorContext;
@Slf4j @Slf4j
public class StringLengthValidator implements ConstraintValidator<Length, String> { public class StringLengthValidator implements ConstraintValidator<Length, Object> {
private int max; private int max;
@Override @Override
public boolean isValid(String value, ConstraintValidatorContext context) { public boolean isValid(Object value, ConstraintValidatorContext context) {
if (StringUtils.isEmpty(value)) { String stringValue;
if (value instanceof CharSequence || value instanceof JsonNode) {
stringValue = value.toString();
} else {
return true; return true;
} }
return value.length() <= max; if (StringUtils.isEmpty(stringValue)) {
return true;
}
return stringValue.length() <= max;
} }
@Override @Override

7
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java

@ -54,8 +54,6 @@ public class JpaAlarmCommentDao extends JpaAbstractDao<AlarmCommentEntity, Alarm
@Autowired @Autowired
private AlarmCommentRepository alarmCommentRepository; private AlarmCommentRepository alarmCommentRepository;
private static final String TABLE_NAME = ALARM_COMMENT_COLUMN_FAMILY_NAME;
@Override @Override
public AlarmComment createAlarmComment(TenantId tenantId, AlarmComment alarmComment){ public AlarmComment createAlarmComment(TenantId tenantId, AlarmComment alarmComment){
log.trace("Saving entity {}", alarmComment); log.trace("Saving entity {}", alarmComment);
@ -89,11 +87,6 @@ public class JpaAlarmCommentDao extends JpaAbstractDao<AlarmCommentEntity, Alarm
return findByIdAsync(tenantId, key); return findByIdAsync(tenantId, key);
} }
@Override
public void cleanUpAlarmComments(long expTime) {
partitioningRepository.dropPartitionsBefore(TABLE_NAME, expTime, TimeUnit.HOURS.toMillis(partitionSizeInHours));
}
@Override @Override
protected Class<AlarmCommentEntity> getEntityClass() { protected Class<AlarmCommentEntity> getEntityClass() {
return AlarmCommentEntity.class; return AlarmCommentEntity.class;

2
dao/src/main/resources/sql/schema-entities-idx.sql

@ -80,4 +80,4 @@ CREATE INDEX IF NOT EXISTS idx_rule_node_type ON rule_node(type);
CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id); CREATE INDEX IF NOT EXISTS idx_api_usage_state_entity_id ON api_usage_state(entity_id);
CREATE INDEX IF NOT EXISTS idx_alarm_comment_alarm_id ON alarm_comment(alarm_id ASC); CREATE INDEX IF NOT EXISTS idx_alarm_comment_alarm_id ON alarm_comment(alarm_id);

22
dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmCommentServiceTest.java

@ -39,6 +39,8 @@ import org.thingsboard.server.common.data.security.Authority;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import static org.thingsboard.server.common.data.alarm.AlarmCommentType.OTHER;
public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest { public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
public static final String TEST_ALARM = "TEST_ALARM"; public static final String TEST_ALARM = "TEST_ALARM";
@ -80,18 +82,18 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
public void testSaveAndFetchAlarmComment() throws ExecutionException, InterruptedException { public void testSaveAndFetchAlarmComment() throws ExecutionException, InterruptedException {
AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId()) AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId())
.userId(user.getId()) .userId(user.getId())
.type("OTHER") .type(OTHER)
.comment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10))) .comment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10)))
.build(); .build();
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment).getAlarmComment(); AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment);
Assert.assertNotNull(createdComment); Assert.assertNotNull(createdComment);
Assert.assertNotNull(createdComment.getId()); Assert.assertNotNull(createdComment.getId());
Assert.assertEquals(alarm.getId(), createdComment.getAlarmId()); Assert.assertEquals(alarm.getId(), createdComment.getAlarmId());
Assert.assertEquals(user.getId(), createdComment.getUserId()); Assert.assertEquals(user.getId(), createdComment.getUserId());
Assert.assertEquals("OTHER", createdComment.getType()); Assert.assertEquals(OTHER, createdComment.getType());
Assert.assertTrue(createdComment.getCreatedTime() > 0); Assert.assertTrue(createdComment.getCreatedTime() > 0);
AlarmComment fetched = alarmCommentService.findAlarmCommentByIdAsync(tenantId, createdComment.getId()).get(); AlarmComment fetched = alarmCommentService.findAlarmCommentByIdAsync(tenantId, createdComment.getId()).get();
@ -108,11 +110,11 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
UserId userId = new UserId(UUID.randomUUID()); UserId userId = new UserId(UUID.randomUUID());
AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId()) AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId())
.userId(userId) .userId(userId)
.type("OTHER") .type(OTHER)
.comment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10))) .comment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10)))
.build(); .build();
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment).getAlarmComment(); AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment);
Assert.assertNotNull(createdComment); Assert.assertNotNull(createdComment);
Assert.assertNotNull(createdComment.getId()); Assert.assertNotNull(createdComment.getId());
@ -120,11 +122,11 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
//update comment //update comment
String newComment = "new comment"; String newComment = "new comment";
createdComment.setComment(JacksonUtil.newObjectNode().put("text", newComment)); createdComment.setComment(JacksonUtil.newObjectNode().put("text", newComment));
AlarmComment updatedComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, createdComment).getAlarmComment(); AlarmComment updatedComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, createdComment);
Assert.assertEquals(alarm.getId(), updatedComment.getAlarmId()); Assert.assertEquals(alarm.getId(), updatedComment.getAlarmId());
Assert.assertEquals(userId, updatedComment.getUserId()); Assert.assertEquals(userId, updatedComment.getUserId());
Assert.assertEquals("OTHER", updatedComment.getType()); Assert.assertEquals(OTHER, updatedComment.getType());
Assert.assertTrue(updatedComment.getCreatedTime() > 0); Assert.assertTrue(updatedComment.getCreatedTime() > 0);
Assert.assertEquals(newComment, updatedComment.getComment().get("text").asText()); Assert.assertEquals(newComment, updatedComment.getComment().get("text").asText());
Assert.assertEquals("true", updatedComment.getComment().get("edited").asText()); Assert.assertEquals("true", updatedComment.getComment().get("edited").asText());
@ -144,16 +146,16 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
UserId userId = new UserId(UUID.randomUUID()); UserId userId = new UserId(UUID.randomUUID());
AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId()) AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId())
.userId(userId) .userId(userId)
.type("OTHER") .type(OTHER)
.comment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10))) .comment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10)))
.build(); .build();
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment).getAlarmComment(); AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment);
Assert.assertNotNull(createdComment); Assert.assertNotNull(createdComment);
Assert.assertNotNull(createdComment.getId()); Assert.assertNotNull(createdComment.getId());
Assert.assertTrue("Alarm comment was not deleted when expected", alarmCommentService.deleteAlarmComment(tenantId, createdComment.getId()).isSuccessful()); alarmCommentService.deleteAlarmComment(tenantId, createdComment.getId());
AlarmComment fetched = alarmCommentService.findAlarmCommentByIdAsync(tenantId, createdComment.getId()).get(); AlarmComment fetched = alarmCommentService.findAlarmCommentByIdAsync(tenantId, createdComment.getId()).get();

9
dao/src/test/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDaoTest.java

@ -22,6 +22,7 @@ import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmComment; import org.thingsboard.server.common.data.alarm.AlarmComment;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmCommentId;
import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AlarmId;
@ -58,9 +59,9 @@ public class JpaAlarmCommentDaoTest extends AbstractJpaDaoTest {
saveAlarm(alarmId1, UUID.randomUUID(), UUID.randomUUID(), "TEST_ALARM"); saveAlarm(alarmId1, UUID.randomUUID(), UUID.randomUUID(), "TEST_ALARM");
saveAlarm(alarmId2, UUID.randomUUID(), UUID.randomUUID(), "TEST_ALARM"); saveAlarm(alarmId2, UUID.randomUUID(), UUID.randomUUID(), "TEST_ALARM");
saveAlarmComment(commentId1, alarmId1, userId, "OTHER"); saveAlarmComment(commentId1, alarmId1, userId, AlarmCommentType.OTHER);
saveAlarmComment(commentId2, alarmId1, userId, "OTHER"); saveAlarmComment(commentId2, alarmId1, userId, AlarmCommentType.OTHER);
saveAlarmComment(commentId3, alarmId2, userId, "OTHER"); saveAlarmComment(commentId3, alarmId2, userId, AlarmCommentType.OTHER);
int count = alarmCommentDao.findAlarmComments(TenantId.fromUUID(tenantId), new AlarmId(alarmId1), new PageLink(10, 0)).getData().size(); int count = alarmCommentDao.findAlarmComments(TenantId.fromUUID(tenantId), new AlarmId(alarmId1), new PageLink(10, 0)).getData().size();
assertEquals(2, count); assertEquals(2, count);
@ -78,7 +79,7 @@ public class JpaAlarmCommentDaoTest extends AbstractJpaDaoTest {
alarm.setStatus(AlarmStatus.ACTIVE_UNACK); alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
alarmDao.save(TenantId.fromUUID(tenantId), alarm); alarmDao.save(TenantId.fromUUID(tenantId), alarm);
} }
private void saveAlarmComment(UUID id, UUID alarmId, UUID userId, String type) { private void saveAlarmComment(UUID id, UUID alarmId, UUID userId, AlarmCommentType type) {
AlarmComment alarmComment = new AlarmComment(); AlarmComment alarmComment = new AlarmComment();
alarmComment.setId(new AlarmCommentId(id)); alarmComment.setId(new AlarmCommentId(id));
alarmComment.setAlarmId(TenantId.fromUUID(alarmId)); alarmComment.setAlarmId(TenantId.fromUUID(alarmId));

Loading…
Cancel
Save