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')")
@RequestMapping(value = "/alarm/{alarmId}/comment/{commentId}", method = RequestMethod.DELETE)
@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);
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
Alarm alarm = checkAlarmId(alarmId, Operation.DELETE);
AlarmCommentId alarmCommentId = new AlarmCommentId(toUUID(strCommentId));
AlarmComment alarmComment = checkAlarmCommentId(alarmCommentId);
return tbAlarmCommentService.deleteAlarmComment(alarm, alarmComment, getCurrentUser());
AlarmComment alarmComment = checkAlarmCommentId(alarmCommentId, alarmId);
tbAlarmCommentService.deleteAlarmComment(alarm, alarmComment, getCurrentUser());
}
@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 {
validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId);
AlarmComment alarmComment = alarmCommentService.findAlarmCommentByIdAsync(getCurrentUser().getTenantId(), alarmCommentId).get();
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) {
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();
alarmComment.setUserId(userId);
try {
AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment).getAlarmComment());
AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.createOrUpdateAlarmComment(alarm.getTenantId(), alarmComment));
notificationEntityService.notifyCreateOrUpdateAlarmComment(alarm, savedAlarmComment, actionType, user);
return savedAlarmComment;
} catch (Exception e) {
@ -45,8 +45,8 @@ public class DefaultTbAlarmCommentService extends AbstractTbEntityService implem
}
@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);
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.User;
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.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
@ -56,6 +58,12 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
long ackTs = System.currentTimeMillis();
ListenableFuture<Boolean> future = alarmSubscriptionService.ackAlarm(alarm.getTenantId(), alarm.getId(), ackTs);
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.setStatus(alarm.getStatus().isCleared() ? AlarmStatus.CLEARED_ACK : AlarmStatus.ACTIVE_ACK);
notificationEntityService.notifyCreateOrUpdateAlarm(alarm, ActionType.ALARM_ACK, user);
@ -68,6 +76,12 @@ public class DefaultTbAlarmService extends AbstractTbEntityService implements Tb
long clearTs = System.currentTimeMillis();
ListenableFuture<Boolean> future = alarmSubscriptionService.clearAlarm(alarm.getTenantId(), alarm.getId(), null, clearTs);
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.setStatus(alarm.getStatus().isAck() ? AlarmStatus.CLEARED_ACK : AlarmStatus.CLEARED_UNACK);
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 {
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() {
super();
put(Resource.ALARM, customerAlarmPermissionChecker);
put(Resource.ALARM_COMMENT, customerAlarmPermissionChecker);
put(Resource.ASSET, customerEntityPermissionChecker);
put(Resource.DEVICE, customerEntityPermissionChecker);
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 {
ADMIN_SETTINGS(),
ALARM(EntityType.ALARM),
ALARM_COMMENT(EntityType.ALARM_COMMENT),
DEVICE(EntityType.DEVICE),
ASSET(EntityType.ASSET),
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();
put(Resource.ADMIN_SETTINGS, PermissionChecker.allowAllPermissionChecker);
put(Resource.ALARM, tenantEntityPermissionChecker);
put(Resource.ALARM_COMMENT, tenantEntityPermissionChecker);
put(Resource.ASSET, tenantEntityPermissionChecker);
put(Resource.DEVICE, 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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.ApiUsageRecordKey;
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.AlarmQuery;
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.TopicPartitionInfo;
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.AlarmService;
import org.thingsboard.server.gen.transport.TransportProtos;
@ -61,6 +65,7 @@ import java.util.Optional;
public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService implements AlarmSubscriptionService {
private final AlarmService alarmService;
private final AlarmCommentService alarmCommentService;
private final TbApiUsageReportClient apiUsageClient;
private final TbApiUsageStateService apiUsageStateService;
@ -68,11 +73,13 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
PartitionService partitionService,
AlarmService alarmService,
TbApiUsageReportClient apiUsageClient,
TbApiUsageStateService apiUsageStateService) {
TbApiUsageStateService apiUsageStateService,
AlarmCommentService alarmCommentService) {
super(clusterService, partitionService);
this.alarmService = alarmService;
this.apiUsageClient = apiUsageClient;
this.apiUsageStateService = apiUsageStateService;
this.alarmCommentService = alarmCommentService;
}
@Autowired(required = false)
@ -90,6 +97,15 @@ public class DefaultAlarmSubscriptionService extends AbstractSubscriptionService
AlarmOperationResult result = alarmService.createOrUpdateAlarm(alarm, apiUsageStateService.getApiUsageState(alarm.getTenantId()).isAlarmCreationEnabled());
if (result.isSuccessful()) {
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()) {
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:
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
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:
enabled: "${SQL_TTL_RPC_ENABLED:true}"
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.AlarmComment;
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.AlarmStatus;
import org.thingsboard.server.common.data.audit.ActionType;
@ -115,7 +116,7 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
Mockito.reset(tbClusterService, auditLogService);
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);
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.id.AlarmCommentId;
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.AlarmService;
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.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -77,7 +77,7 @@ public class DefaultTbAlarmCommentServiceTest {
public void testSave() throws ThingsboardException {
var alarm = new Alarm();
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());
verify(notificationEntityService, times(1)).notifyCreateOrUpdateAlarmComment(any(), any(), any(), any());
@ -88,7 +88,7 @@ public class DefaultTbAlarmCommentServiceTest {
var alarmId = new AlarmId(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());
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.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.EntityDaoService;
public interface AlarmCommentService {
AlarmCommentOperationResult createOrUpdateAlarmComment(TenantId tenantId, AlarmComment alarmComment);
public interface AlarmCommentService extends EntityDaoService {
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);

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 org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.id.EntityId;
import java.util.Collections;
@ -27,6 +28,7 @@ public class AlarmOperationResult {
private final Alarm alarm;
private final boolean successful;
private final boolean created;
private final AlarmSeverity oldSeverity;
private final List<EntityId> propagatedEntitiesList;
public AlarmOperationResult(Alarm alarm, boolean successful) {
@ -34,13 +36,14 @@ public class AlarmOperationResult {
}
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.successful = successful;
this.created = created;
this.oldSeverity = oldSeverity;
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;
@ApiModelProperty(position = 4, value = "JSON object with User id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
private UserId userId;
@ApiModelProperty(position = 5, value = "Defines origination of comment", example = "System/Other", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
@NoXss
@Length(fieldName = "type")
private String type;
@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)
private AlarmCommentType type;
@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;
@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);
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.fasterxml.jackson.databind.JsonNode;
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.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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.AlarmCommentInfo;
import org.thingsboard.server.common.data.alarm.AlarmCommentType;
import org.thingsboard.server.common.data.id.AlarmCommentId;
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.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.user.UserService;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import static org.thingsboard.server.dao.service.Validator.validateId;
@ -49,15 +48,12 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
@Autowired
private AlarmCommentDao alarmCommentDao;
@Autowired
private UserService userService;
@Autowired
private DataValidator<AlarmComment> alarmCommentDataValidator;
@Override
public AlarmCommentOperationResult createOrUpdateAlarmComment(TenantId tenantId, AlarmComment alarmComment) {
alarmCommentDataValidator.validate(alarmComment, tenantId);
public AlarmComment createOrUpdateAlarmComment(TenantId tenantId, AlarmComment alarmComment) {
alarmCommentDataValidator.validate(alarmComment, c -> tenantId);
if (alarmComment.getId() == null) {
return createAlarmComment(tenantId, alarmComment);
} else {
@ -66,11 +62,9 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
}
@Override
public AlarmCommentOperationResult deleteAlarmComment(TenantId tenantId, AlarmCommentId alarmCommentId) {
public void deleteAlarmComment(TenantId tenantId, AlarmCommentId alarmCommentId) {
log.debug("Deleting Alarm Comment with id: {}", alarmCommentId);
AlarmCommentOperationResult result = new AlarmCommentOperationResult(new AlarmComment(), true);
alarmCommentDao.deleteAlarmComment(tenantId, alarmCommentId);
return result;
}
@Override
@ -93,21 +87,20 @@ public class BaseAlarmCommentService extends AbstractEntityService implements Al
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);
if (alarmComment.getType() == null) {
alarmComment.setType("OTHER");
alarmComment.setType(AlarmCommentType.OTHER);
}
if (alarmComment.getId() == null) {
UUID uuid = Uuids.timeBased();
alarmComment.setId(new AlarmCommentId(uuid));
alarmComment.setCreatedTime(Uuids.unixTimestamp(uuid));
}
AlarmComment saved = alarmCommentDao.createAlarmComment(tenantId, alarmComment);
return new AlarmCommentOperationResult(saved, true, true);
return alarmCommentDao.createAlarmComment(tenantId, alarmComment);
}
private AlarmCommentOperationResult updateAlarmComment(TenantId tenantId, AlarmComment newAlarmComment) {
private AlarmComment updateAlarmComment(TenantId tenantId, AlarmComment newAlarmComment) {
log.debug("Update Alarm comment : {}", newAlarmComment);
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));
existing.setComment(comment);
}
AlarmComment result = alarmCommentDao.save(tenantId, existing);
return new AlarmCommentOperationResult(result, true, true);
return alarmCommentDao.save(tenantId, existing);
}
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);
Alarm saved = alarmDao.save(alarm.getTenantId(), alarm);
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 {
@ -208,6 +208,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
boolean propagationEnabled = !oldAlarm.isPropagate() && newAlarm.isPropagate();
boolean propagationToOwnerEnabled = !oldAlarm.isPropagateToOwner() && newAlarm.isPropagateToOwner();
boolean propagationToTenantEnabled = !oldAlarm.isPropagateToTenant() && newAlarm.isPropagateToTenant();
AlarmSeverity oldAlarmSeverity = oldAlarm.getSeverity();
Alarm result = alarmDao.save(newAlarm.getTenantId(), merge(oldAlarm, newAlarm));
List<EntityId> propagatedEntitiesList;
if (propagationEnabled || propagationToOwnerEnabled || propagationToTenantEnabled) {
@ -220,7 +221,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
} else {
propagatedEntitiesList = new ArrayList<>(getPropagationEntityIds(result));
}
return new AlarmOperationResult(result, true, propagatedEntitiesList);
return new AlarmOperationResult(result, true, false, oldAlarmSeverity, propagatedEntitiesList);
}
@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.TypeDef;
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.AlarmId;
import org.thingsboard.server.common.data.id.UserId;
@ -50,7 +51,7 @@ public abstract class AbstractAlarmCommentEntity<T extends AlarmComment> extends
private UUID userId;
@Column(name = ALARM_COMMENT_TYPE)
private String type;
private AlarmCommentType type;
@Type(type = "json")
@Column(name = ALARM_COMMENT_COMMENT)
@ -65,9 +66,7 @@ public abstract class AbstractAlarmCommentEntity<T extends AlarmComment> extends
this.setUuid(alarmComment.getUuidId());
}
this.setCreatedTime(alarmComment.getCreatedTime());
if (alarmComment.getAlarmId() != null) {
this.alarmId = alarmComment.getAlarmId().getId();
}
this.alarmId = alarmComment.getAlarmId().getId();
if (alarmComment.getUserId() != null) {
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) {
}

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

@ -15,6 +15,7 @@
*/
package org.thingsboard.server.dao.service;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.validation.Length;
@ -23,15 +24,21 @@ import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
@Slf4j
public class StringLengthValidator implements ConstraintValidator<Length, String> {
public class StringLengthValidator implements ConstraintValidator<Length, Object> {
private int max;
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isEmpty(value)) {
public boolean isValid(Object value, ConstraintValidatorContext context) {
String stringValue;
if (value instanceof CharSequence || value instanceof JsonNode) {
stringValue = value.toString();
} else {
return true;
}
return value.length() <= max;
if (StringUtils.isEmpty(stringValue)) {
return true;
}
return stringValue.length() <= max;
}
@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
private AlarmCommentRepository alarmCommentRepository;
private static final String TABLE_NAME = ALARM_COMMENT_COLUMN_FAMILY_NAME;
@Override
public AlarmComment createAlarmComment(TenantId tenantId, AlarmComment alarmComment){
log.trace("Saving entity {}", alarmComment);
@ -89,11 +87,6 @@ public class JpaAlarmCommentDao extends JpaAbstractDao<AlarmCommentEntity, Alarm
return findByIdAsync(tenantId, key);
}
@Override
public void cleanUpAlarmComments(long expTime) {
partitioningRepository.dropPartitionsBefore(TABLE_NAME, expTime, TimeUnit.HOURS.toMillis(partitionSizeInHours));
}
@Override
protected Class<AlarmCommentEntity> getEntityClass() {
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_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.concurrent.ExecutionException;
import static org.thingsboard.server.common.data.alarm.AlarmCommentType.OTHER;
public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
public static final String TEST_ALARM = "TEST_ALARM";
@ -80,18 +82,18 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
public void testSaveAndFetchAlarmComment() throws ExecutionException, InterruptedException {
AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId())
.userId(user.getId())
.type("OTHER")
.type(OTHER)
.comment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10)))
.build();
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment).getAlarmComment();
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment);
Assert.assertNotNull(createdComment);
Assert.assertNotNull(createdComment.getId());
Assert.assertEquals(alarm.getId(), createdComment.getAlarmId());
Assert.assertEquals(user.getId(), createdComment.getUserId());
Assert.assertEquals("OTHER", createdComment.getType());
Assert.assertEquals(OTHER, createdComment.getType());
Assert.assertTrue(createdComment.getCreatedTime() > 0);
AlarmComment fetched = alarmCommentService.findAlarmCommentByIdAsync(tenantId, createdComment.getId()).get();
@ -108,11 +110,11 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
UserId userId = new UserId(UUID.randomUUID());
AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId())
.userId(userId)
.type("OTHER")
.type(OTHER)
.comment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10)))
.build();
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment).getAlarmComment();
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment);
Assert.assertNotNull(createdComment);
Assert.assertNotNull(createdComment.getId());
@ -120,11 +122,11 @@ public abstract class BaseAlarmCommentServiceTest extends AbstractServiceTest {
//update comment
String newComment = "new comment";
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(userId, updatedComment.getUserId());
Assert.assertEquals("OTHER", updatedComment.getType());
Assert.assertEquals(OTHER, updatedComment.getType());
Assert.assertTrue(updatedComment.getCreatedTime() > 0);
Assert.assertEquals(newComment, updatedComment.getComment().get("text").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());
AlarmComment alarmComment = AlarmComment.builder().alarmId(alarm.getId())
.userId(userId)
.type("OTHER")
.type(OTHER)
.comment(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10)))
.build();
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment).getAlarmComment();
AlarmComment createdComment = alarmCommentService.createOrUpdateAlarmComment(tenantId, alarmComment);
Assert.assertNotNull(createdComment);
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();

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.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.id.AlarmCommentId;
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(alarmId2, UUID.randomUUID(), UUID.randomUUID(), "TEST_ALARM");
saveAlarmComment(commentId1, alarmId1, userId, "OTHER");
saveAlarmComment(commentId2, alarmId1, userId, "OTHER");
saveAlarmComment(commentId3, alarmId2, userId, "OTHER");
saveAlarmComment(commentId1, alarmId1, userId, AlarmCommentType.OTHER);
saveAlarmComment(commentId2, alarmId1, userId, AlarmCommentType.OTHER);
saveAlarmComment(commentId3, alarmId2, userId, AlarmCommentType.OTHER);
int count = alarmCommentDao.findAlarmComments(TenantId.fromUUID(tenantId), new AlarmId(alarmId1), new PageLink(10, 0)).getData().size();
assertEquals(2, count);
@ -78,7 +79,7 @@ public class JpaAlarmCommentDaoTest extends AbstractJpaDaoTest {
alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
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.setId(new AlarmCommentId(id));
alarmComment.setAlarmId(TenantId.fromUUID(alarmId));

Loading…
Cancel
Save