From 8a157397d48a2d0a96c6bd44debcee39a0874671 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 8 Dec 2022 17:04:30 +0200 Subject: [PATCH] added rest controller for alarm comments --- .../main/data/upgrade/3.5/schema_update.sql | 28 +++++ .../server/actors/ActorSystemContext.java | 5 + .../actors/ruleChain/DefaultTbContext.java | 6 + .../controller/AlarmCommentController.java | 108 ++++++++++++++++ .../server/controller/BaseController.java | 22 +++- .../controller/ControllerConstants.java | 2 + .../entitiy/AbstractTbEntityService.java | 3 + .../DefaultTbNotificationEntityService.java | 13 ++ .../entitiy/TbNotificationEntityService.java | 4 + .../alarm/DefaultTbAlarmCommentService.java | 50 ++++++++ .../entitiy/alarm/TbAlarmCommentService.java | 26 ++++ .../permission/CustomerUserPermissions.java | 14 +++ .../service/security/permission/Resource.java | 1 + .../permission/TenantAdminPermissions.java | 1 + .../src/main/resources/thingsboard.yml | 2 + .../alarm/AlarmCommentOperationResult.java | 37 ++++++ .../server/dao/alarm/AlarmCommentService.java | 35 ++++++ .../server/dao/alarm/AlarmService.java | 2 +- .../server/common/data/EntityType.java | 2 +- .../common/data/alarm/AlarmComment.java | 91 ++++++++++++++ .../common/data/alarm/AlarmCommentInfo.java | 67 ++++++++++ .../server/common/data/id/AlarmCommentId.java | 45 +++++++ .../common/data/id/EntityIdFactory.java | 2 + .../server/dao/alarm/AlarmCommentDao.java | 39 ++++++ .../dao/alarm/BaseAlarmCommentService.java | 105 ++++++++++++++++ .../server/dao/model/ModelConstants.java | 9 ++ .../dao/model/sql/AlarmCommentEntity.java | 119 ++++++++++++++++++ .../validator/AlarmCommentDataValidator.java | 44 +++++++ .../dao/sql/alarm/AlarmCommentRepository.java | 29 +++++ .../dao/sql/alarm/JpaAlarmCommentDao.java | 101 +++++++++++++++ .../rule/engine/api/TbContext.java | 3 + .../util/EntitiesFieldsAsyncLoader.java | 4 + 32 files changed, 1016 insertions(+), 3 deletions(-) create mode 100644 application/src/main/data/upgrade/3.5/schema_update.sql create mode 100644 application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentOperationResult.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/AlarmCommentId.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmCommentEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmCommentRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java diff --git a/application/src/main/data/upgrade/3.5/schema_update.sql b/application/src/main/data/upgrade/3.5/schema_update.sql new file mode 100644 index 0000000000..506ac7e38c --- /dev/null +++ b/application/src/main/data/upgrade/3.5/schema_update.sql @@ -0,0 +1,28 @@ +-- +-- 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. +-- + +CREATE TABLE IF NOT EXISTS alarm_comment ( + id uuid NOT NULL, + tenant_id uuid NOT NULL, + customer_id uuid NOT NULL, + created_time bigint NOT NULL, + alarm_id uuid NOT NULL, + user_id uuid, + type varchar(255) NOT NULL, + comment varchar(1000000), + CONSTRAINT fk_alarm_comment_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE +) PARTITION BY RANGE (created_time); +CREATE INDEX IF NOT EXISTS idx_alarm_comment_alarm_id ON alarm_comment(alarm_id ASC); \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index e1a41fe401..82f9ec25b6 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -50,6 +50,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.common.msg.tools.TbRateLimits; import org.thingsboard.server.common.stats.TbApiUsageReportClient; +import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.audit.AuditLogService; @@ -268,6 +269,10 @@ public class ActorSystemContext { @Getter private AlarmSubscriptionService alarmService; + @Autowired + @Getter + private AlarmCommentService alarmCommentService; + @Autowired @Getter private JsInvokeService jsInvokeService; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index ef2de35912..826883aee2 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -71,6 +71,7 @@ import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.common.msg.TbMsgProcessingStackItem; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; +import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.cassandra.CassandraCluster; @@ -579,6 +580,11 @@ class DefaultTbContext implements TbContext { return mainCtx.getAlarmService(); } + @Override + public AlarmCommentService getAlarmCommentService() { + return mainCtx.getAlarmCommentService(); + } + @Override public RuleChainService getRuleChainService() { return mainCtx.getRuleChainService(); diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java new file mode 100644 index 0000000000..c4c1bc8de2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmCommentController.java @@ -0,0 +1,108 @@ +/** + * 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.controller; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +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.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.alarm.TbAlarmCommentService; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; + +import static org.thingsboard.server.controller.ControllerConstants.ALARM_ID_PARAM_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; +import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; + +@RestController +@TbCoreComponent +@RequiredArgsConstructor +@RequestMapping("/api") +public class AlarmCommentController extends BaseController { + public static final String ALARM_ID = "alarmId"; + public static final String ALARM_COMMENT_ID = "commentId"; + + private final TbAlarmCommentService tbAlarmCommentService; + + @ApiOperation(value = "Create or update Alarm Comment ", + notes = "Creates or Updates the Alarm Comment. " + + "When creating comment, platform generates Alarm Comment Id as " + UUID_WIKI_LINK + + "The newly created Alarm Comment id will be present in the response. Specify existing Alarm Comment id to update the alarm. " + + "Referencing non-existing Alarm Comment Id will cause 'Not Found' error. " + + "\nRemove 'id' and optionally 'userId' from the request body example (below) to create new Alarm comment entity. " + + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH + , produces = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.POST) + @ResponseBody + public AlarmComment saveAlarmComment(@ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) + @PathVariable(ALARM_ID) String strAlarmId, @ApiParam(value = "A JSON value representing the comment.") @RequestBody AlarmComment alarmComment) throws ThingsboardException { + checkParameter(ALARM_ID, strAlarmId); + alarmComment.setTenantId(getTenantId()); + alarmComment.setAlarmId(new AlarmId(toUUID(strAlarmId))); + checkEntity(alarmComment.getId(), alarmComment, Resource.ALARM_COMMENT); + return tbAlarmCommentService.saveAlarmComment(alarmComment, getCurrentUser()); + } + + @ApiOperation(value = "Delete Alarm comment(deleteAlarmComment)", + notes = "Deletes the Alarm comment. Referencing non-existing Alarm comment Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE) + @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_ID_PARAM_DESCRIPTION) @PathVariable(ALARM_COMMENT_ID) String strCommentId) throws ThingsboardException { + checkParameter(ALARM_ID, strAlarmId); + AlarmCommentId alarmCommentId = new AlarmCommentId(toUUID(strAlarmId)); + AlarmComment alarmComment = checkAlarmCommentId(alarmCommentId, Operation.DELETE); + return tbAlarmCommentService.deleteAlarmComment(alarmComment, getCurrentUser()); + } + + @ApiOperation(value = "Get Alarm comments (getAlarmComments)", + notes = "Returns a page of alarm comments. " + + PAGE_DATA_PARAMETERS + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, produces = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/alarm/{alarmId}/comment", method = RequestMethod.GET) + @ResponseBody + public PageData getAlarmComments( + @ApiParam(value = ALARM_ID_PARAM_DESCRIPTION) + @PathVariable(ALARM_ID) String strAlarmCommentId, + @ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) + @RequestParam int pageSize, + @ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) + @RequestParam int page + ) throws Exception { + AlarmId alarmId = new AlarmId(toUUID(strAlarmCommentId)); + PageLink pageLink = createPageLink(pageSize, page, null, null, null); + return checkNotNull(alarmCommentService.findAlarmComments(alarmId, pageLink).get()); + } +} diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index ffc3fcf3cd..965842af46 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -54,6 +54,7 @@ import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.TenantProfile; 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.AlarmInfo; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetInfo; @@ -64,6 +65,7 @@ import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 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.common.data.id.AssetId; import org.thingsboard.server.common.data.id.AssetProfileId; @@ -98,6 +100,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; +import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; @@ -131,7 +134,6 @@ import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.component.ComponentDiscoveryService; -import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.edge.rpc.EdgeRpcService; import org.thingsboard.server.service.entitiy.TbNotificationEntityService; import org.thingsboard.server.service.ota.OtaPackageStateService; @@ -200,6 +202,9 @@ public abstract class BaseController { @Autowired protected AlarmSubscriptionService alarmService; + @Autowired + protected AlarmCommentService alarmCommentService; + @Autowired protected DeviceCredentialsService deviceCredentialsService; @@ -531,6 +536,9 @@ public abstract class BaseController { case ALARM: checkAlarmId(new AlarmId(entityId.getId()), operation); return; + case ALARM_COMMENT: + checkAlarmCommentId(new AlarmCommentId(entityId.getId()), operation); + return; case DEVICE: checkDeviceId(new DeviceId(entityId.getId()), operation); return; @@ -713,6 +721,18 @@ public abstract class BaseController { } } + AlarmComment checkAlarmCommentId(AlarmCommentId alarmCommentId, Operation operation) 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"); + accessControlService.checkPermission(getCurrentUser(), Resource.ALARM_COMMENT, operation, alarmCommentId, alarmComment); + return alarmComment; + } catch (Exception e) { + throw handleException(e, false); + } + } + WidgetsBundle checkWidgetsBundleId(WidgetsBundleId widgetsBundleId, Operation operation) throws ThingsboardException { try { validateId(widgetsBundleId, "Incorrect widgetsBundleId " + widgetsBundleId); diff --git a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java index e00ccaa4bc..5e7622ece5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java +++ b/application/src/main/java/org/thingsboard/server/controller/ControllerConstants.java @@ -44,6 +44,8 @@ public class ControllerConstants { protected static final String USER_ID_PARAM_DESCRIPTION = "A string value representing the user id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String ASSET_ID_PARAM_DESCRIPTION = "A string value representing the asset id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String ALARM_ID_PARAM_DESCRIPTION = "A string value representing the alarm id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; + + protected static final String ALARM_COMMENT_ID_PARAM_DESCRIPTION = "A string value representing the alarm comment id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String ENTITY_ID_PARAM_DESCRIPTION = "A string value representing the entity id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String OTA_PACKAGE_ID_PARAM_DESCRIPTION = "A string value representing the ota package id. For example, '784f394c-42b6-435a-983c-b7beff2784f9'"; protected static final String ENTITY_TYPE_PARAM_DESCRIPTION = "A string value representing the entity type. For example, 'DEVICE'"; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java index 9f34c061fe..10af591647 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/AbstractTbEntityService.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.TimePageLink; +import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.edge.EdgeService; @@ -65,6 +66,8 @@ public abstract class AbstractTbEntityService { @Autowired protected AlarmSubscriptionService alarmSubscriptionService; @Autowired + protected AlarmCommentService alarmCommentService; + @Autowired protected CustomerService customerService; @Autowired protected TbClusterService tbClusterService; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java index 8f8531a5b4..0bcd605928 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/DefaultTbNotificationEntityService.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.Tenant; 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.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEventActionType; @@ -229,6 +230,18 @@ public class DefaultTbNotificationEntityService implements TbNotificationEntityS sendEntityNotificationMsg(alarm.getTenantId(), alarm.getId(), edgeTypeByActionType(actionType)); } + @Override + public void notifyCreateOrUpdateAlarmComment(AlarmComment alarmComment, ActionType actionType, User user, Object... additionalInfo) { + logEntityAction(alarmComment.getTenantId(), alarmComment.getAlarmId(), alarmComment, alarmComment.getCustomerId(), actionType, user, additionalInfo); + // TODO: should we send notification to edge? + } + + @Override + public void notifyDeleteAlarmComment(AlarmComment alarmComment, User user, Object... additionalInfo) { + logEntityAction(alarmComment.getTenantId(), alarmComment.getAlarmId(), alarmComment, alarmComment.getCustomerId(), ActionType.DELETED, user, additionalInfo); + // TODO: should we send notification to edge? + } + @Override public void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId, I entityId, E entity, User user, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java index dc6e148cab..6719394d5b 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/TbNotificationEntityService.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.Tenant; 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.audit.ActionType; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeEventActionType; @@ -102,6 +103,9 @@ public interface TbNotificationEntityService { void notifyCreateOrUpdateAlarm(Alarm alarm, ActionType actionType, User user, Object... additionalInfo); + void notifyCreateOrUpdateAlarmComment(AlarmComment alarmComment, ActionType actionType, User user, Object... additionalInfo); + + void notifyDeleteAlarmComment(AlarmComment alarmComment, User user, Object... additionalInfo); void notifyCreateOrUpdateOrDelete(TenantId tenantId, CustomerId customerId, I entityId, E entity, User user, ActionType actionType, boolean sendNotifyMsgToEdge, diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java new file mode 100644 index 0000000000..e0bfaf10e4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmCommentService.java @@ -0,0 +1,50 @@ +/** + * 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.entitiy.alarm; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; + +@Service +@AllArgsConstructor +public class DefaultTbAlarmCommentService extends AbstractTbEntityService implements TbAlarmCommentService{ + @Override + public AlarmComment saveAlarmComment(AlarmComment alarmComment, User user) throws ThingsboardException { + ActionType actionType = alarmComment.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + UserId userId = user.getId(); + alarmComment.setUserId(userId); + try { + AlarmComment savedAlarmComment = checkNotNull(alarmCommentService.createOrUpdateAlarmComment(alarmComment).getAlarmComment()); + notificationEntityService.notifyCreateOrUpdateAlarmComment(savedAlarmComment, actionType, user); + return savedAlarmComment; + } catch (Exception e) { + notificationEntityService.logEntityAction(alarmComment.getTenantId(), emptyId(EntityType.ALARM_COMMENT), alarmComment, actionType, user, e); + throw e; + } + } + @Override + public Boolean deleteAlarmComment(AlarmComment alarmComment, User user) { + notificationEntityService.notifyDeleteAlarmComment(alarmComment, user); + return alarmCommentService.deleteAlarmComment(alarmComment.getId()).isSuccessful(); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java new file mode 100644 index 0000000000..375b655af8 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/alarm/TbAlarmCommentService.java @@ -0,0 +1,26 @@ +/** + * 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.entitiy.alarm; + +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.exception.ThingsboardException; + +public interface TbAlarmCommentService { + AlarmComment saveAlarmComment(AlarmComment alarmComment, User user) throws ThingsboardException; + + Boolean deleteAlarmComment(AlarmComment alarmComment, User user); +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java index cdd84b0478..489d9deb81 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java @@ -32,6 +32,7 @@ public class CustomerUserPermissions extends AbstractPermissions { public CustomerUserPermissions() { super(); put(Resource.ALARM, customerAlarmPermissionChecker); + put(Resource.ALARM_COMMENT, customerAlarmCommentPermissionChecker); put(Resource.ASSET, customerEntityPermissionChecker); put(Resource.DEVICE, customerEntityPermissionChecker); put(Resource.CUSTOMER, customerPermissionChecker); @@ -59,6 +60,19 @@ public class CustomerUserPermissions extends AbstractPermissions { } }; + private static final PermissionChecker customerAlarmCommentPermissionChecker = new PermissionChecker() { + @Override + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { + if (!user.getTenantId().equals(entity.getTenantId())) { + return false; + } + if (!(entity instanceof HasCustomerId)) { + return false; + } + return user.getCustomerId().equals(((HasCustomerId) entity).getCustomerId()); + } + }; + private static final PermissionChecker customerEntityPermissionChecker = new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_CREDENTIALS, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES, diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index a9484a2bd3..0979c0969d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -22,6 +22,7 @@ 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), diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index ef7991e5de..7027fda414 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -30,6 +30,7 @@ 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); diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 6a00775a38..67f9928c18 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -277,6 +277,8 @@ sql: partition_size: "${SQL_EDGE_EVENTS_PARTITION_SIZE_HOURS:168}" # Number of hours to partition the events. The current value corresponds to one week. audit_logs: partition_size: "${SQL_AUDIT_LOGS_PARTITION_SIZE_HOURS:168}" # Default value - 1 week + alarm_comments: + partition_size: "${SQL_ALARM_COMMENTS_PARTITION_SIZE_HOURS:168}" # Default value - 1 week # Specify whether to sort entities before batch update. Should be enabled for cluster mode to avoid deadlocks batch_sort: "${SQL_BATCH_SORT:true}" # Specify whether to remove null characters from strValue of attributes and timeseries before insert diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentOperationResult.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentOperationResult.java new file mode 100644 index 0000000000..3687a03eca --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentOperationResult.java @@ -0,0 +1,37 @@ +/** + * 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; + } +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java new file mode 100644 index 0000000000..83e6216800 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentService.java @@ -0,0 +1,35 @@ +/** + * 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 com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.id.AlarmCommentId; +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; + +public interface AlarmCommentService { + AlarmCommentOperationResult createOrUpdateAlarmComment(AlarmComment alarmComment); + + AlarmCommentOperationResult deleteAlarmComment(AlarmCommentId alarmCommentId); + + ListenableFuture> findAlarmComments(AlarmId alarmId, PageLink pageLink); + + ListenableFuture findAlarmCommentByIdAsync(TenantId tenantId, AlarmCommentId alarmCommentId); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java index b8041aa3fc..b5bbdc761c 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java @@ -18,12 +18,12 @@ package org.thingsboard.server.dao.alarm; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.alarm.Alarm; -import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index 0798647903..44b175c20d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, ASSET_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC, QUEUE; + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, ALARM_COMMENT, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, ASSET_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE, RPC, QUEUE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java new file mode 100644 index 0000000000..ce2d11d12f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmComment.java @@ -0,0 +1,91 @@ +/** + * 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; + +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasCustomerId; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.AlarmCommentId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; + +@ApiModel +@Data +@Builder +@AllArgsConstructor +public class AlarmComment extends BaseData implements HasName, HasTenantId, HasCustomerId { + @ApiModelProperty(position = 3, value = "JSON object with Tenant Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private TenantId tenantId; + + @ApiModelProperty(position = 4, value = "JSON object with Customer Id", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private CustomerId customerId; + @ApiModelProperty(position = 2, value = "JSON object with Alarm id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private EntityId alarmId; + @ApiModelProperty(position = 3, value = "JSON object with User id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private UserId userId; + @ApiModelProperty(position = 4, value = "Defines origination of comment", example = "System/Other", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + private String type; + @ApiModelProperty(position = 5, value = "JSON object with text of comment.", dataType = "com.fasterxml.jackson.databind.JsonNode") + private transient JsonNode comment; + + @ApiModelProperty(position = 1, value = "JSON object with the alarm comment Id. " + + "Specify this field to update the alarm comment. " + + "Referencing non-existing alarm Id will cause error. " + + "Omit this field to create new alarm." ) + @Override + public AlarmCommentId getId() { + return super.getId(); + } + + @ApiModelProperty(position = 2, value = "Timestamp of the alarm comment creation, in milliseconds", example = "1634058704567", accessMode = ApiModelProperty.AccessMode.READ_ONLY) + @Override + public long getCreatedTime() { + return super.getCreatedTime(); + } + + public AlarmComment() { + super(); + } + + public AlarmComment(AlarmCommentId id) { + super(id); + } + + @Override + public String getName() { + return comment.toString(); + } + + public AlarmComment(AlarmComment alarmComment) { + super(alarmComment.getId()); + this.createdTime = alarmComment.getCreatedTime(); + this.tenantId = alarmComment.getTenantId(); + this.customerId = alarmComment.getCustomerId(); + this.type = alarmComment.getType(); + this.comment = alarmComment.getComment(); + this.userId = alarmComment.getUserId(); + + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentInfo.java new file mode 100644 index 0000000000..7cbff38d80 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmCommentInfo.java @@ -0,0 +1,67 @@ +/** + * 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; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +@ApiModel +public class AlarmCommentInfo extends AlarmComment{ + private static final long serialVersionUID = 2807343093519543377L; + + @ApiModelProperty(position = 19, value = "Alarm originator name", example = "Thermostat") + private String userName; + + public AlarmCommentInfo() { + super(); + } + + public AlarmCommentInfo(AlarmComment alarmComment) { + super(alarmComment); + } + + public AlarmCommentInfo(AlarmComment alarmComment, String userName) { + super(alarmComment); + this.userName = userName; + } + + public String getUserName() { + return userName; + } + + public void setOriginatorName(String originatorName) { + this.userName = originatorName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + AlarmCommentInfo alarmCommentInfo = (AlarmCommentInfo) o; + + return userName != null ? userName.equals(alarmCommentInfo.userName) : alarmCommentInfo.userName == null; + + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (userName != null ? userName.hashCode() : 0); + return result; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/AlarmCommentId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/AlarmCommentId.java new file mode 100644 index 0000000000..b13b5837a0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/AlarmCommentId.java @@ -0,0 +1,45 @@ +/** + * 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.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +@ApiModel +public class AlarmCommentId extends UUIDBased implements EntityId{ + + private static final long serialVersionUID = 2L; + + @JsonCreator + public AlarmCommentId(@JsonProperty("id") UUID id) { + super(id); + } + + public static AlarmCommentId fromString(String commentId) { + return new AlarmCommentId(UUID.fromString(commentId)); + } + + @ApiModelProperty(position = 2, required = true, value = "string", example = "ALARM_COMMENT", allowableValues = "ALARM_COMMENT") + @Override + public EntityType getEntityType() { + return EntityType.ALARM_COMMENT; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index c6a19e0040..ae647825cd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -53,6 +53,8 @@ public class EntityIdFactory { return new AssetId(uuid); case ALARM: return new AlarmId(uuid); + case ALARM_COMMENT: + return new AlarmCommentId(uuid); case RULE_CHAIN: return new RuleChainId(uuid); case RULE_NODE: diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java new file mode 100644 index 0000000000..e58ca14664 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmCommentDao.java @@ -0,0 +1,39 @@ +/** + * 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 com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.id.AlarmCommentId; +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.Dao; + +import java.util.UUID; + +public interface AlarmCommentDao extends Dao { + AlarmComment createAlarmComment(AlarmComment alarmComment); + void deleteAlarmComment(AlarmCommentId alarmCommentId); + + AlarmComment findAlarmCommentById(TenantId tenantId, UUID key); + + PageData findAlarmComments(AlarmId id, PageLink pageLink); + + ListenableFuture findAlarmCommentByIdAsync(TenantId tenantId, UUID key); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java new file mode 100644 index 0000000000..d293531dd4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmCommentService.java @@ -0,0 +1,105 @@ +/** + * 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 com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +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.alarm.AlarmComment; +import org.thingsboard.server.common.data.id.AlarmCommentId; +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.AbstractEntityService; +import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.dao.service.DataValidator; + +import static org.thingsboard.server.dao.service.Validator.validateId; + +@Service +@Slf4j +public class BaseAlarmCommentService extends AbstractEntityService implements AlarmCommentService{ + + @Autowired + private AlarmCommentDao alarmCommentDao; + @Autowired + private EntityService entityService; + @Autowired + private DataValidator alarmCommentDataValidator; + @Override + public AlarmCommentOperationResult createOrUpdateAlarmComment(AlarmComment alarmComment) { + alarmCommentDataValidator.validate(alarmComment, AlarmComment::getTenantId); + alarmComment.setCustomerId(entityService.fetchEntityCustomerId(alarmComment.getTenantId(), alarmComment.getAlarmId())); + if (alarmComment.getId() == null) { + return createAlarmComment(alarmComment); + } else { + return updateAlarmComment(alarmComment); + } + } + + @Override + @Transactional + public AlarmCommentOperationResult deleteAlarmComment(AlarmCommentId alarmCommentId) { + log.debug("Deleting Alarm Comment with id: {}", alarmCommentId); + AlarmCommentOperationResult result = new AlarmCommentOperationResult(new AlarmComment(), true); + alarmCommentDao.deleteAlarmComment(alarmCommentId); + return result; + } + + @Override + public ListenableFuture> findAlarmComments(AlarmId alarmId, PageLink pageLink) { + PageData alarmComments = alarmCommentDao.findAlarmComments(alarmId, pageLink); + return Futures.immediateFuture(alarmComments); + } + + @Override + public ListenableFuture findAlarmCommentByIdAsync(TenantId tenantId, AlarmCommentId alarmCommentId) { + log.trace("Executing findAlarmCommentByIdAsync [{}]", alarmCommentId); + validateId(alarmCommentId, "Incorrect alarmId " + alarmCommentId); + return alarmCommentDao.findAlarmCommentByIdAsync(tenantId, alarmCommentId.getId()); + } + + private AlarmCommentOperationResult createAlarmComment(AlarmComment alarmComment) { + log.debug("New Alarm comment : {}", alarmComment); + if (alarmComment.getType() == null) { + alarmComment.setType("OTHER"); + } + AlarmComment saved = alarmCommentDao.createAlarmComment(alarmComment); + return new AlarmCommentOperationResult(saved, true, true); + } + + private AlarmCommentOperationResult updateAlarmComment(AlarmComment newAlarmComment) { + log.debug("Update Alarm comment : {}", newAlarmComment); + + validateId(newAlarmComment.getId(), "Alarm comment id should be specified!"); + AlarmComment existing = alarmCommentDao.findAlarmCommentById(newAlarmComment.getTenantId(), newAlarmComment.getId().getId()); + if (existing != null) { + AlarmComment result = alarmCommentDao.save(newAlarmComment.getTenantId(), merge(existing, newAlarmComment)); + return new AlarmCommentOperationResult(result, true, true); + } + return null; + } + + private AlarmComment merge(AlarmComment existing, AlarmComment alarmComment) { + existing.setCustomerId(alarmComment.getCustomerId()); + existing.setComment(alarmComment.getComment()); + return existing; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index e24bde91fb..bd462eb6b6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -297,6 +297,15 @@ public class ModelConstants { public static final String ALARM_BY_ID_VIEW_NAME = "alarm_by_id"; + public static final String ALARM_COMMENT_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + public static final String ALARM_COMMENT_CUSTOMER_ID_PROPERTY = CUSTOMER_ID_PROPERTY; + public static final String ALARM_COMMENT_COLUMN_FAMILY_NAME = "alarm_comment"; + public static final String ALARM_COMMENT_ALARM_ID = "alarm_id"; + public static final String ALARM_COMMENT_USER_ID = USER_ID_PROPERTY; + public static final String ALARM_COMMENT_TYPE = "type"; + public static final String ALARM_COMMENT_COMMENT = "comment"; + + /** * Cassandra entity relation constants. */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmCommentEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmCommentEntity.java new file mode 100644 index 0000000000..571c3007e1 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmCommentEntity.java @@ -0,0 +1,119 @@ +/** + * 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.model.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +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.id.AlarmCommentId; +import org.thingsboard.server.common.data.id.AlarmId; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.dao.model.BaseEntity; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_ALARM_ID; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_COLUMN_FAMILY_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_COMMENT; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_CUSTOMER_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_TENANT_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_TYPE; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ALARM_COMMENT_COLUMN_FAMILY_NAME) + +public class AlarmCommentEntity extends BaseSqlEntity implements BaseEntity { + + @Column(name = ALARM_COMMENT_TENANT_ID_PROPERTY, columnDefinition = "uuid") + private UUID tenantId; + + @Column(name = ALARM_COMMENT_CUSTOMER_ID_PROPERTY, columnDefinition = "uuid") + private UUID customerId; + @Column(name = ALARM_COMMENT_ALARM_ID, columnDefinition = "uuid") + private UUID alarmId; + + @Column(name = ModelConstants.ALARM_COMMENT_USER_ID) + private UUID userId; + + @Column(name = ALARM_COMMENT_TYPE) + private String type; + + @Type(type = "json") + @Column(name = ALARM_COMMENT_COMMENT) + private JsonNode comment; + + public AlarmCommentEntity() { + super(); + } + + public AlarmCommentEntity(AlarmComment alarmComment) { + if (alarmComment.getId() != null) { + this.setUuid(alarmComment.getUuidId()); + } + if (alarmComment.getTenantId() != null) { + this.tenantId = alarmComment.getTenantId().getId(); + } + if (alarmComment.getCustomerId() != null) { + this.customerId = alarmComment.getCustomerId().getId(); + } + this.setCreatedTime(alarmComment.getCreatedTime()); + this.setCreatedTime(alarmComment.getCreatedTime()); + if (alarmComment.getAlarmId() != null) { + this.alarmId = alarmComment.getAlarmId().getId(); + } + if (alarmComment.getUserId() != null) { + this.userId = alarmComment.getUserId().getId(); + } + if (alarmComment.getType() != null) { + this.type = alarmComment.getType(); + } + this.setComment(alarmComment.getComment()); + } + + @Override + public AlarmComment toData() { + AlarmComment alarmComment = new AlarmComment(new AlarmCommentId(id)); + alarmComment.setCreatedTime(createdTime); + alarmComment.setAlarmId(new AlarmId(alarmId)); + if (userId != null) { + alarmComment.setUserId(new UserId(userId)); + } + if (tenantId != null) { + alarmComment.setTenantId(TenantId.fromUUID(tenantId)); + } + if (customerId != null) { + alarmComment.setCustomerId(new CustomerId(customerId)); + } + alarmComment.setType(type); + alarmComment.setComment(comment); + return alarmComment; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java new file mode 100644 index 0000000000..9969de6fe2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/AlarmCommentDataValidator.java @@ -0,0 +1,44 @@ +/** + * 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.service.validator; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.tenant.TenantService; + +@Component +@AllArgsConstructor +public class AlarmCommentDataValidator extends DataValidator { + + private final TenantService tenantService; + + @Override + protected void validateDataImpl(TenantId tenantId, AlarmComment alarmComment) { + if (alarmComment.getTenantId() == null) { + throw new DataValidationException("Alarm comment should be assigned to tenant!"); + } else { + if (!tenantService.tenantExists(alarmComment.getTenantId())) { + throw new DataValidationException("Alarm comment is referencing to non-existent tenant!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmCommentRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmCommentRepository.java new file mode 100644 index 0000000000..1517b35554 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmCommentRepository.java @@ -0,0 +1,29 @@ +/** + * 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.sql.alarm; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.thingsboard.server.dao.model.sql.AlarmCommentEntity; + +import java.util.UUID; + +public interface AlarmCommentRepository extends JpaRepository { + + Page findAllByAlarmId(UUID alarmId, Pageable pageable); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java new file mode 100644 index 0000000000..440510f37b --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmCommentDao.java @@ -0,0 +1,101 @@ +/** + * 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.sql.alarm; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.id.AlarmCommentId; +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.DaoUtil; +import org.thingsboard.server.dao.alarm.AlarmCommentDao; +import org.thingsboard.server.dao.model.sql.AlarmCommentEntity; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.sqlts.insert.sql.SqlPartitioningRepository; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COMMENT_COLUMN_FAMILY_NAME; + +@Slf4j +@Component +@SqlDao +@RequiredArgsConstructor +public class JpaAlarmCommentDao extends JpaAbstractDao implements AlarmCommentDao { + private final SqlPartitioningRepository partitioningRepository; + @Value("${sql.alarm_comments.partition_size:168}") + private int partitionSizeInHours; + + @Autowired + private AlarmCommentRepository alarmCommentRepository; + + @Override + public AlarmComment createAlarmComment(AlarmComment alarmComment){ + log.debug("Saving entity {}", alarmComment); + if (alarmComment.getId() == null) { + UUID uuid = Uuids.timeBased(); + alarmComment.setId(new AlarmCommentId(uuid)); + alarmComment.setCreatedTime(Uuids.unixTimestamp(uuid)); + } + partitioningRepository.createPartitionIfNotExists(ALARM_COMMENT_COLUMN_FAMILY_NAME, alarmComment.getCreatedTime(), TimeUnit.HOURS.toMillis(partitionSizeInHours)); + AlarmCommentEntity saved = alarmCommentRepository.save(new AlarmCommentEntity(alarmComment)); + return DaoUtil.getData(saved); + } + + @Override + public void deleteAlarmComment(AlarmCommentId alarmCommentId){ + log.trace("Try to delete entity alarm comment by id using [{}]", alarmCommentId); + alarmCommentRepository.deleteById(alarmCommentId.getId()); + } + + @Override + public PageData findAlarmComments(AlarmId id, PageLink pageLink){ + log.trace("Try to find alarm comments by alard id using [{}]", id); + return DaoUtil.toPageData( + alarmCommentRepository.findAllByAlarmId(id.getId(), DaoUtil.toPageable(pageLink))); + } + + @Override + public AlarmComment findAlarmCommentById(TenantId tenantId, UUID key) { + return findById(tenantId, key); + } + + @Override + public ListenableFuture findAlarmCommentByIdAsync(TenantId tenantId, UUID key) { + return findByIdAsync(tenantId, key); + } + + @Override + protected Class getEntityClass() { + return AlarmCommentEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return alarmCommentRepository; + } +} diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 9851eead80..3f18d165de 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.rule.RuleNodeState; import org.thingsboard.server.common.data.script.ScriptLanguage; import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.dao.alarm.AlarmCommentService; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.cassandra.CassandraCluster; @@ -231,6 +232,8 @@ public interface TbContext { RuleEngineAlarmService getAlarmService(); + AlarmCommentService getAlarmCommentService(); + RuleChainService getRuleChainService(); RuleEngineRpcService getRpcService(); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java index 245b671cf5..558e197064 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/util/EntitiesFieldsAsyncLoader.java @@ -22,6 +22,7 @@ import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityFieldsData; +import org.thingsboard.server.common.data.id.AlarmCommentId; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; @@ -56,6 +57,9 @@ public class EntitiesFieldsAsyncLoader { case ALARM: return getAsync(ctx.getAlarmService().findAlarmByIdAsync(ctx.getTenantId(), (AlarmId) original), EntityFieldsData::new); + case ALARM_COMMENT: + return getAsync(ctx.getAlarmCommentService().findAlarmCommentByIdAsync(ctx.getTenantId(), (AlarmCommentId) original), + EntityFieldsData::new); case RULE_CHAIN: return getAsync(ctx.getRuleChainService().findRuleChainByIdAsync(ctx.getTenantId(), (RuleChainId) original), EntityFieldsData::new);