From b7f19a12cfa82f84e2cfb2c465f0e91acf0c3862 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 12 May 2023 14:54:14 +0300 Subject: [PATCH 1/4] Version control for notification configs --- .../main/data/upgrade/3.5.0/schema_update.sql | 53 +++++++ .../DefaultEntitiesExportImportService.java | 3 +- .../DefaultExportableEntitiesService.java | 58 +------ .../impl/BaseEntityExportService.java | 11 ++ .../impl/NotificationRuleExportService.java | 103 ++++++++++++ .../impl/NotificationTargetExportService.java | 55 +++++++ .../NotificationTemplateExportService.java | 42 +++++ .../impl/NotificationRuleImportService.java | 150 ++++++++++++++++++ .../impl/NotificationTargetImportService.java | 109 +++++++++++++ .../NotificationTemplateImportService.java | 72 +++++++++ .../server/dao/entity/EntityDaoService.java | 4 + .../notification/rule/NotificationRule.java | 6 +- .../targets/NotificationTarget.java | 16 +- .../template/NotificationTemplate.java | 17 +- .../template/NotificationTemplateConfig.java | 9 ++ .../server/common/data/sync/JsonTbEntity.java | 8 +- .../org/thingsboard/server/dao/DaoUtil.java | 8 + .../dao/asset/AssetProfileServiceImpl.java | 6 + .../server/dao/asset/BaseAssetService.java | 6 + .../dao/customer/CustomerServiceImpl.java | 6 + .../dao/dashboard/DashboardServiceImpl.java | 6 + .../dao/device/DeviceProfileServiceImpl.java | 6 + .../server/dao/device/DeviceServiceImpl.java | 6 + .../server/dao/model/BaseSqlEntity.java | 7 +- .../dao/model/sql/NotificationRuleEntity.java | 6 + .../model/sql/NotificationTargetEntity.java | 5 + .../model/sql/NotificationTemplateEntity.java | 5 + .../DefaultNotificationRuleService.java | 5 + .../DefaultNotificationTargetService.java | 5 + .../DefaultNotificationTemplateService.java | 5 + .../dao/notification/NotificationRuleDao.java | 3 +- .../notification/NotificationTargetDao.java | 3 +- .../notification/NotificationTemplateDao.java | 4 +- .../server/dao/rule/BaseRuleChainService.java | 6 + .../notification/JpaNotificationRuleDao.java | 26 ++- .../JpaNotificationTargetDao.java | 20 +++ .../JpaNotificationTemplateDao.java | 21 +++ .../NotificationRuleRepository.java | 10 +- .../NotificationTargetRepository.java | 10 +- .../NotificationTemplateRepository.java | 10 +- .../dao/widget/WidgetsBundleServiceImpl.java | 5 + .../main/resources/sql/schema-entities.sql | 12 +- 42 files changed, 854 insertions(+), 74 deletions(-) create mode 100644 application/src/main/data/upgrade/3.5.0/schema_update.sql create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTargetExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTemplateExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTemplateImportService.java diff --git a/application/src/main/data/upgrade/3.5.0/schema_update.sql b/application/src/main/data/upgrade/3.5.0/schema_update.sql new file mode 100644 index 0000000000..c80f5d8ba5 --- /dev/null +++ b/application/src/main/data/upgrade/3.5.0/schema_update.sql @@ -0,0 +1,53 @@ +-- +-- Copyright © 2016-2023 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. +-- + +-- NOTIFICATION CONFIGS VERSION CONTROL START + +ALTER TABLE notification_template + ADD COLUMN IF NOT EXISTS external_id UUID; +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'uq_notification_template_external_id') THEN + ALTER TABLE notification_template ADD CONSTRAINT uq_notification_template_external_id UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +ALTER TABLE notification_target + ADD COLUMN IF NOT EXISTS external_id UUID; +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'uq_notification_target_external_id') THEN + ALTER TABLE notification_target ADD CONSTRAINT uq_notification_target_external_id UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +ALTER TABLE notification_rule + ADD COLUMN IF NOT EXISTS external_id UUID; +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'uq_notification_rule_external_id') THEN + ALTER TABLE notification_rule ADD CONSTRAINT uq_notification_rule_external_id UNIQUE (tenant_id, external_id); + END IF; + END; +$$; + +-- NOTIFICATION CONFIGS VERSION CONTROL END + diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java index 822bd82e00..f1855d963d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/DefaultEntitiesExportImportService.java @@ -67,7 +67,8 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS protected static final List SUPPORTED_ENTITY_TYPES = List.of( EntityType.CUSTOMER, EntityType.ASSET_PROFILE, EntityType.ASSET, EntityType.RULE_CHAIN, EntityType.DASHBOARD, EntityType.DEVICE_PROFILE, EntityType.DEVICE, - EntityType.ENTITY_VIEW, EntityType.WIDGETS_BUNDLE + EntityType.ENTITY_VIEW, EntityType.WIDGETS_BUNDLE, + EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_RULE ); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java index 456eeb8b86..3358e5182f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/DefaultExportableEntitiesService.java @@ -22,36 +22,21 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasTenantId; -import org.thingsboard.server.common.data.id.AssetId; -import org.thingsboard.server.common.data.id.AssetProfileId; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DashboardId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; -import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.WidgetsBundleId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; -import org.thingsboard.server.dao.asset.AssetProfileService; -import org.thingsboard.server.dao.asset.AssetService; -import org.thingsboard.server.dao.customer.CustomerService; -import org.thingsboard.server.dao.dashboard.DashboardService; -import org.thingsboard.server.dao.device.DeviceProfileService; -import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.rule.RuleChainService; -import org.thingsboard.server.dao.widget.WidgetsBundleService; +import org.thingsboard.server.dao.entity.EntityDaoService; +import org.thingsboard.server.dao.entity.EntityServiceRegistry; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.AccessControlService; import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.function.BiConsumer; @Service @TbCoreComponent @@ -60,11 +45,10 @@ import java.util.function.BiConsumer; public class DefaultExportableEntitiesService implements ExportableEntitiesService { private final Map> daos = new HashMap<>(); - private final Map> removers = new HashMap<>(); + private final EntityServiceRegistry entityServiceRegistry; private final AccessControlService accessControlService; - @Override public , I extends EntityId> E findEntityByTenantIdAndExternalId(TenantId tenantId, I externalId) { EntityType entityType = externalId.getEntityType(); @@ -152,11 +136,11 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi @Override public void removeById(TenantId tenantId, I id) { EntityType entityType = id.getEntityType(); - BiConsumer entityRemover = removers.get(entityType); - if (entityRemover == null) { + EntityDaoService entityService = entityServiceRegistry.getServiceByEntityType(entityType); + if (entityService == null) { throw new IllegalArgumentException("Unsupported entity type " + entityType); } - entityRemover.accept(tenantId, id); + entityService.deleteEntity(tenantId, id); } private > ExportableEntityDao getExportableEntityDao(EntityType entityType) { @@ -182,34 +166,4 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi }); } - @Autowired - private void setRemovers(CustomerService customerService, AssetService assetService, RuleChainService ruleChainService, - DashboardService dashboardService, DeviceProfileService deviceProfileService, - AssetProfileService assetProfileService, DeviceService deviceService, WidgetsBundleService widgetsBundleService) { - removers.put(EntityType.CUSTOMER, (tenantId, entityId) -> { - customerService.deleteCustomer(tenantId, (CustomerId) entityId); - }); - removers.put(EntityType.ASSET, (tenantId, entityId) -> { - assetService.deleteAsset(tenantId, (AssetId) entityId); - }); - removers.put(EntityType.RULE_CHAIN, (tenantId, entityId) -> { - ruleChainService.deleteRuleChainById(tenantId, (RuleChainId) entityId); - }); - removers.put(EntityType.DASHBOARD, (tenantId, entityId) -> { - dashboardService.deleteDashboard(tenantId, (DashboardId) entityId); - }); - removers.put(EntityType.DEVICE_PROFILE, (tenantId, entityId) -> { - deviceProfileService.deleteDeviceProfile(tenantId, (DeviceProfileId) entityId); - }); - removers.put(EntityType.ASSET_PROFILE, (tenantId, entityId) -> { - assetProfileService.deleteAssetProfile(tenantId, (AssetProfileId) entityId); - }); - removers.put(EntityType.DEVICE, (tenantId, entityId) -> { - deviceService.deleteDevice(tenantId, (DeviceId) entityId); - }); - removers.put(EntityType.WIDGETS_BUNDLE, (tenantId, entityId) -> { - widgetsBundleService.deleteWidgetsBundle(tenantId, (WidgetsBundleId) entityId); - }); - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java index 891a6411a3..09b96d68fa 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/BaseEntityExportService.java @@ -24,7 +24,11 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.sync.ie.EntityExportData; import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; +import java.util.Collection; import java.util.Set; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Stream; public abstract class BaseEntityExportService, D extends EntityExportData> extends DefaultEntityExportService { @@ -47,4 +51,11 @@ public abstract class BaseEntityExportService getExternalIdOrElseInternalByUuid(ctx, uuid)); } + protected Stream toExternalIds(Collection internalIds, Function entityIdCreator, + EntitiesExportCtx ctx) { + return internalIds.stream().map(entityIdCreator) + .map(entityId -> getExternalIdOrElseInternal(ctx, entityId)) + .map(EntityId::getId); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java new file mode 100644 index 0000000000..0906fb83ef --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationRuleExportService.java @@ -0,0 +1,103 @@ +/** + * Copyright © 2016-2023 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.sync.ie.exporting.impl; + +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.NotificationRuleId; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.notification.rule.DefaultNotificationRuleRecipientsConfig; +import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleRecipientsConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.sync.ie.EntityExportData; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +@TbCoreComponent +public class NotificationRuleExportService, D extends EntityExportData> extends BaseEntityExportService> { + + @Override + protected void setRelatedEntities(EntitiesExportCtx ctx, NotificationRule notificationRule, EntityExportData exportData) { + notificationRule.setTemplateId(getExternalIdOrElseInternal(ctx, notificationRule.getTemplateId())); + + NotificationRuleTriggerConfig ruleTriggerConfig = notificationRule.getTriggerConfig(); + switch (ruleTriggerConfig.getTriggerType()) { + case DEVICE_ACTIVITY: { + DeviceActivityNotificationRuleTriggerConfig triggerConfig = (DeviceActivityNotificationRuleTriggerConfig) ruleTriggerConfig; + Set devices = triggerConfig.getDevices(); + if (devices != null) { + triggerConfig.setDevices(toExternalIds(devices, DeviceId::new, ctx).collect(Collectors.toSet())); + } + + Set deviceProfiles = triggerConfig.getDeviceProfiles(); + if (deviceProfiles != null) { + triggerConfig.setDeviceProfiles(toExternalIds(deviceProfiles, DeviceProfileId::new, ctx).collect(Collectors.toSet())); + } + break; + } + case RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT: + RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig triggerConfig = (RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig) ruleTriggerConfig; + Set ruleChains = triggerConfig.getRuleChains(); + if (ruleChains != null) { + triggerConfig.setRuleChains(toExternalIds(ruleChains, RuleChainId::new, ctx).collect(Collectors.toSet())); + } + break; + } + + NotificationRuleRecipientsConfig ruleRecipientsConfig = notificationRule.getRecipientsConfig(); + switch (ruleTriggerConfig.getTriggerType()) { + case ALARM: { + EscalatedNotificationRuleRecipientsConfig recipientsConfig = (EscalatedNotificationRuleRecipientsConfig) ruleRecipientsConfig; + Map> escalationTable = new LinkedHashMap<>(recipientsConfig.getEscalationTable()); + escalationTable.replaceAll((delay, targets) -> { + return toExternalIds(targets, NotificationTargetId::new, ctx).collect(Collectors.toList()); + }); + recipientsConfig.setEscalationTable(escalationTable); + break; + } + default: { + DefaultNotificationRuleRecipientsConfig recipientsConfig = (DefaultNotificationRuleRecipientsConfig) ruleRecipientsConfig; + List targets = recipientsConfig.getTargets(); + targets = toExternalIds(targets, NotificationTargetId::new, ctx).collect(Collectors.toList()); + recipientsConfig.setTargets(targets); + break; + } + } + } + + @Override + public Set getSupportedEntityTypes() { + return Set.of(EntityType.NOTIFICATION_RULE); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTargetExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTargetExportService.java new file mode 100644 index 0000000000..f0a5da3efc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTargetExportService.java @@ -0,0 +1,55 @@ +/** + * Copyright © 2016-2023 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.sync.ie.exporting.impl; + +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; +import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; +import org.thingsboard.server.common.data.sync.ie.EntityExportData; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; + +import java.util.Set; + +@Service +@TbCoreComponent +public class NotificationTargetExportService extends BaseEntityExportService> { + + @Override + protected void setRelatedEntities(EntitiesExportCtx ctx, NotificationTarget notificationTarget, EntityExportData exportData) { + if (notificationTarget.getConfiguration().getType() == NotificationTargetType.PLATFORM_USERS) { + UsersFilter usersFilter = ((PlatformUsersNotificationTargetConfig) notificationTarget.getConfiguration()).getUsersFilter(); + switch (usersFilter.getType()) { + case CUSTOMER_USERS: + CustomerUsersFilter customerUsersFilter = (CustomerUsersFilter) usersFilter; + customerUsersFilter.setCustomerId(getExternalIdOrElseInternal(ctx, new CustomerId(customerUsersFilter.getCustomerId())).getId()); + break; + } + } + } + + @Override + public Set getSupportedEntityTypes() { + return Set.of(EntityType.NOTIFICATION_TARGET); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTemplateExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTemplateExportService.java new file mode 100644 index 0000000000..d28f352bae --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/exporting/impl/NotificationTemplateExportService.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2023 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.sync.ie.exporting.impl; + +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.NotificationTemplateId; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.sync.ie.EntityExportData; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; + +import java.util.Set; + +@Service +@TbCoreComponent +public class NotificationTemplateExportService extends BaseEntityExportService> { + + @Override + protected void setRelatedEntities(EntitiesExportCtx ctx, NotificationTemplate notificationTemplate, EntityExportData exportData) { + + } + + @Override + public Set getSupportedEntityTypes() { + return Set.of(EntityType.NOTIFICATION_TEMPLATE); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java new file mode 100644 index 0000000000..a7d7670c8c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationRuleImportService.java @@ -0,0 +1,150 @@ +/** + * Copyright © 2016-2023 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.sync.ie.importing.impl; + +import lombok.RequiredArgsConstructor; +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.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.NotificationRuleId; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.notification.rule.DefaultNotificationRuleRecipientsConfig; +import org.thingsboard.server.common.data.notification.rule.EscalatedNotificationRuleRecipientsConfig; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.rule.NotificationRuleRecipientsConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.DeviceActivityNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.common.data.notification.rule.trigger.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.sync.ie.EntityExportData; +import org.thingsboard.server.dao.notification.NotificationRuleService; +import org.thingsboard.server.dao.service.ConstraintValidator; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class NotificationRuleImportService extends BaseEntityImportService> { + + private final NotificationRuleService notificationRuleService; + + @Override + protected void setOwner(TenantId tenantId, NotificationRule notificationRule, IdProvider idProvider) { + notificationRule.setTenantId(tenantId); + } + + @Override + protected NotificationRule prepare(EntitiesImportCtx ctx, NotificationRule notificationRule, NotificationRule oldNotificationRule, EntityExportData exportData, IdProvider idProvider) { + notificationRule.setTemplateId(idProvider.getInternalId(notificationRule.getTemplateId())); + + NotificationRuleTriggerConfig ruleTriggerConfig = notificationRule.getTriggerConfig(); + NotificationRuleTriggerType triggerType = ruleTriggerConfig.getTriggerType(); + switch (triggerType) { + case DEVICE_ACTIVITY: { + DeviceActivityNotificationRuleTriggerConfig triggerConfig = (DeviceActivityNotificationRuleTriggerConfig) ruleTriggerConfig; + Set devices = triggerConfig.getDevices(); + if (devices != null) { + triggerConfig.setDevices(devices.stream().map(DeviceId::new) + .map(idProvider::getInternalId).map(UUIDBased::getId) + .collect(Collectors.toSet())); + } + + Set deviceProfiles = triggerConfig.getDeviceProfiles(); + if (deviceProfiles != null) { + triggerConfig.setDeviceProfiles(deviceProfiles.stream().map(DeviceProfileId::new) + .map(idProvider::getInternalId).map(UUIDBased::getId) + .collect(Collectors.toSet())); + } + break; + } + case RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT: + RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig triggerConfig = (RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig) ruleTriggerConfig; + Set ruleChains = triggerConfig.getRuleChains(); + if (ruleChains != null) { + triggerConfig.setRuleChains(ruleChains.stream().map(RuleChainId::new) + .map(idProvider::getInternalId).map(UUIDBased::getId) + .collect(Collectors.toSet())); + } + break; + } + if (!triggerType.isTenantLevel()) { + throw new IllegalArgumentException("Trigger type " + triggerType + " is not available for tenants"); + } + + NotificationRuleRecipientsConfig ruleRecipientsConfig = notificationRule.getRecipientsConfig(); + switch (triggerType) { + case ALARM: { + EscalatedNotificationRuleRecipientsConfig recipientsConfig = (EscalatedNotificationRuleRecipientsConfig) ruleRecipientsConfig; + Map> escalationTable = new LinkedHashMap<>(recipientsConfig.getEscalationTable()); + escalationTable.replaceAll((delay, targets) -> targets.stream() + .map(NotificationTargetId::new).map(idProvider::getInternalId) + .map(UUIDBased::getId).collect(Collectors.toList())); + recipientsConfig.setEscalationTable(escalationTable); + break; + } + default: { + DefaultNotificationRuleRecipientsConfig recipientsConfig = (DefaultNotificationRuleRecipientsConfig) ruleRecipientsConfig; + List targets = recipientsConfig.getTargets().stream() + .map(NotificationTargetId::new).map(idProvider::getInternalId) + .map(UUIDBased::getId).collect(Collectors.toList()); + recipientsConfig.setTargets(targets); + break; + } + } + return notificationRule; + } + + @Override + protected NotificationRule saveOrUpdate(EntitiesImportCtx ctx, NotificationRule notificationRule, EntityExportData exportData, IdProvider idProvider) { + ConstraintValidator.validateFields(notificationRule); + return notificationRuleService.saveNotificationRule(ctx.getTenantId(), notificationRule); + } + + @Override + protected void onEntitySaved(User user, NotificationRule savedEntity, NotificationRule oldEntity) throws ThingsboardException { + entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, null, + oldEntity == null ? ActionType.ADDED : ActionType.UPDATED, null); + clusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedEntity.getId(), + oldEntity == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + } + + @Override + protected NotificationRule deepCopy(NotificationRule notificationRule) { + return new NotificationRule(notificationRule); + } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_RULE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java new file mode 100644 index 0000000000..a950a1592e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTargetImportService.java @@ -0,0 +1,109 @@ +/** + * Copyright © 2016-2023 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.sync.ie.importing.impl; + +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections.CollectionUtils; +import org.springframework.security.access.AccessDeniedException; +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.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.NotificationTargetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.common.data.id.UserId; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.targets.NotificationTargetType; +import org.thingsboard.server.common.data.notification.targets.platform.CustomerUsersFilter; +import org.thingsboard.server.common.data.notification.targets.platform.PlatformUsersNotificationTargetConfig; +import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter; +import org.thingsboard.server.common.data.notification.targets.platform.UserListFilter; +import org.thingsboard.server.common.data.notification.targets.platform.UsersFilter; +import org.thingsboard.server.common.data.sync.ie.EntityExportData; +import org.thingsboard.server.dao.notification.NotificationTargetService; +import org.thingsboard.server.dao.service.ConstraintValidator; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; + +import java.util.stream.Collectors; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class NotificationTargetImportService extends BaseEntityImportService> { + + private final NotificationTargetService notificationTargetService; + + @Override + protected void setOwner(TenantId tenantId, NotificationTarget notificationTarget, IdProvider idProvider) { + notificationTarget.setTenantId(tenantId); + } + + @Override + protected NotificationTarget prepare(EntitiesImportCtx ctx, NotificationTarget notificationTarget, NotificationTarget oldNotificationTarget, EntityExportData exportData, IdProvider idProvider) { + if (notificationTarget.getConfiguration().getType() == NotificationTargetType.PLATFORM_USERS) { + UsersFilter usersFilter = ((PlatformUsersNotificationTargetConfig) notificationTarget.getConfiguration()).getUsersFilter(); + switch (usersFilter.getType()) { + case CUSTOMER_USERS: + CustomerUsersFilter customerUsersFilter = (CustomerUsersFilter) usersFilter; + customerUsersFilter.setCustomerId(idProvider.getInternalId(new CustomerId(customerUsersFilter.getCustomerId())).getId()); + break; + case USER_LIST: + UserListFilter userListFilter = (UserListFilter) usersFilter; + userListFilter.setUsersIds(userListFilter.getUsersIds().stream() + .map(UserId::new).map(idProvider::getInternalId) + .map(UUIDBased::getId).collect(Collectors.toList()) + ); + break; + case TENANT_ADMINISTRATORS: + if (CollectionUtils.isNotEmpty(((TenantAdministratorsFilter) usersFilter).getTenantsIds()) || + CollectionUtils.isNotEmpty(((TenantAdministratorsFilter) usersFilter).getTenantProfilesIds())) { + throw new IllegalArgumentException("Permission denied"); + } + break; + case SYSTEM_ADMINISTRATORS: + throw new AccessDeniedException("Permission denied"); + } + } + return notificationTarget; + } + + @Override + protected NotificationTarget saveOrUpdate(EntitiesImportCtx ctx, NotificationTarget notificationTarget, EntityExportData exportData, IdProvider idProvider) { + ConstraintValidator.validateFields(notificationTarget); + return notificationTargetService.saveNotificationTarget(ctx.getTenantId(), notificationTarget); + } + + @Override + protected void onEntitySaved(User user, NotificationTarget savedEntity, NotificationTarget oldEntity) throws ThingsboardException { + entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, null, + oldEntity == null ? ActionType.ADDED : ActionType.UPDATED, null); + } + + @Override + protected NotificationTarget deepCopy(NotificationTarget notificationTarget) { + return new NotificationTarget(notificationTarget); + } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_TARGET; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTemplateImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTemplateImportService.java new file mode 100644 index 0000000000..b98c17a412 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/ie/importing/impl/NotificationTemplateImportService.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2023 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.sync.ie.importing.impl; + +import lombok.RequiredArgsConstructor; +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.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.NotificationTemplateId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; +import org.thingsboard.server.common.data.sync.ie.EntityExportData; +import org.thingsboard.server.dao.notification.NotificationTemplateService; +import org.thingsboard.server.dao.service.ConstraintValidator; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class NotificationTemplateImportService extends BaseEntityImportService> { + + private final NotificationTemplateService notificationTemplateService; + + @Override + protected void setOwner(TenantId tenantId, NotificationTemplate notificationTemplate, IdProvider idProvider) { + notificationTemplate.setTenantId(tenantId); + } + + @Override + protected NotificationTemplate prepare(EntitiesImportCtx ctx, NotificationTemplate notificationTemplate, NotificationTemplate oldEntity, EntityExportData exportData, IdProvider idProvider) { + return notificationTemplate; + } + + @Override + protected NotificationTemplate saveOrUpdate(EntitiesImportCtx ctx, NotificationTemplate notificationTemplate, EntityExportData exportData, IdProvider idProvider) { + ConstraintValidator.validateFields(notificationTemplate); + return notificationTemplateService.saveNotificationTemplate(ctx.getTenantId(), notificationTemplate); + } + + @Override + protected void onEntitySaved(User user, NotificationTemplate savedEntity, NotificationTemplate oldEntity) throws ThingsboardException { + entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, null, + oldEntity == null ? ActionType.ADDED : ActionType.UPDATED, null); + } + + @Override + protected NotificationTemplate deepCopy(NotificationTemplate notificationTemplate) { + return new NotificationTemplate(notificationTemplate); + } + + @Override + public EntityType getEntityType() { + return EntityType.NOTIFICATION_TEMPLATE; + } + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityDaoService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityDaoService.java index e1b8bec633..3094efb5be 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityDaoService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/entity/EntityDaoService.java @@ -30,6 +30,10 @@ public interface EntityDaoService { throw new IllegalArgumentException("Not implemented for " + getEntityType()); } + default void deleteEntity(TenantId tenantId, EntityId id) { + throw new IllegalArgumentException(getEntityType().getNormalName() + " deletion not supported"); + } + EntityType getEntityType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java index 450fbdb23f..73b39c98df 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/NotificationRule.java @@ -20,6 +20,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.NotificationRuleId; @@ -39,7 +40,7 @@ import java.io.Serializable; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) -public class NotificationRule extends BaseData implements HasTenantId, HasName, Serializable { +public class NotificationRule extends BaseData implements HasTenantId, HasName, ExportableEntity, Serializable { private TenantId tenantId; @NotBlank @@ -60,6 +61,8 @@ public class NotificationRule extends BaseData implements Ha private NotificationRuleConfig additionalConfig; + private NotificationRuleId externalId; + public NotificationRule(NotificationRule other) { super(other); this.tenantId = other.tenantId; @@ -69,6 +72,7 @@ public class NotificationRule extends BaseData implements Ha this.triggerConfig = other.triggerConfig; this.recipientsConfig = other.recipientsConfig; this.additionalConfig = other.additionalConfig; + this.externalId = other.externalId; } @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java index 9a2f9a5306..df488d3b1e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/targets/NotificationTarget.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.notification.targets; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.NotificationTargetId; @@ -31,7 +32,7 @@ import javax.validation.constraints.NotNull; @Data @EqualsAndHashCode(callSuper = true) -public class NotificationTarget extends BaseData implements HasTenantId, HasName { +public class NotificationTarget extends BaseData implements HasTenantId, HasName, ExportableEntity { private TenantId tenantId; @NotBlank @@ -42,4 +43,17 @@ public class NotificationTarget extends BaseData implement @Valid private NotificationTargetConfig configuration; + private NotificationTargetId externalId; + + public NotificationTarget() { + } + + public NotificationTarget(NotificationTarget other) { + super(other); + this.tenantId = other.tenantId; + this.name = other.name; + this.configuration = other.configuration; + this.externalId = other.externalId; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java index 71da2f862b..48bc5dd409 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplate.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.notification.template; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.NotificationTemplateId; @@ -32,7 +33,7 @@ import javax.validation.constraints.NotNull; @Data @EqualsAndHashCode(callSuper = true) -public class NotificationTemplate extends BaseData implements HasTenantId, HasName { +public class NotificationTemplate extends BaseData implements HasTenantId, HasName, ExportableEntity { private TenantId tenantId; @NoXss @@ -46,4 +47,18 @@ public class NotificationTemplate extends BaseData imple @NotNull private NotificationTemplateConfig configuration; + private NotificationTemplateId externalId; + + public NotificationTemplate() { + } + + public NotificationTemplate(NotificationTemplate other) { + super(other); + this.tenantId = other.tenantId; + this.name = other.name; + this.notificationType = other.notificationType; + this.configuration = other.configuration != null ? other.configuration.copy() : null; + this.externalId = other.externalId; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java index 1dcb4aec0c..91b10ed2be 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/template/NotificationTemplateConfig.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.notification.NotificationDeliveryMetho import javax.validation.Valid; import javax.validation.constraints.NotEmpty; +import java.util.HashMap; import java.util.Map; @Data @@ -29,4 +30,12 @@ public class NotificationTemplateConfig { @NotEmpty private Map deliveryMethodsTemplates; + public NotificationTemplateConfig copy() { + Map templates = new HashMap<>(deliveryMethodsTemplates); + templates.replaceAll((deliveryMethod, template) -> template.copy()); + NotificationTemplateConfig copy = new NotificationTemplateConfig(); + copy.setDeliveryMethodsTemplates(templates); + return copy; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java index dcb5c29e53..23a98b395b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/sync/JsonTbEntity.java @@ -27,6 +27,9 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetProfile; +import org.thingsboard.server.common.data.notification.rule.NotificationRule; +import org.thingsboard.server.common.data.notification.targets.NotificationTarget; +import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.widget.WidgetsBundle; @@ -48,7 +51,10 @@ import java.lang.annotation.Target; @Type(name = "DASHBOARD", value = Dashboard.class), @Type(name = "CUSTOMER", value = Customer.class), @Type(name = "ENTITY_VIEW", value = EntityView.class), - @Type(name = "WIDGETS_BUNDLE", value = WidgetsBundle.class) + @Type(name = "WIDGETS_BUNDLE", value = WidgetsBundle.class), + @Type(name = "NOTIFICATION_TEMPLATE", value = NotificationTemplate.class), + @Type(name = "NOTIFICATION_TARGET", value = NotificationTarget.class), + @Type(name = "NOTIFICATION_RULE", value = NotificationRule.class) }) @JsonIgnoreProperties(value = {"tenantId", "createdTime"}, ignoreUnknown = true) public @interface JsonTbEntity { diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index 5a328daadb..63b13bca66 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -109,6 +109,14 @@ public abstract class DaoUtil { return ids; } + public static I toEntityId(UUID uuid, Function creator) { + if (uuid != null) { + return creator.apply(uuid); + } else { + return null; + } + } + public static void processInBatches(Function> finder, int batchSize, Consumer processor) { processBatches(finder, batchSize, batch -> batch.getData().forEach(processor)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java index c464d06857..eb297e0049 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileServiceImpl.java @@ -273,6 +273,12 @@ public class AssetProfileServiceImpl extends AbstractCachedEntityService tenantDevicesRemover = new PaginatedRemover<>() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java index 916c459a6b..de3b81ee74 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java @@ -22,6 +22,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UUIDBased; +import org.thingsboard.server.dao.DaoUtil; import javax.persistence.Column; import javax.persistence.Id; @@ -85,11 +86,7 @@ public abstract class BaseSqlEntity implements BaseEntity { } protected static I getEntityId(UUID uuid, Function creator) { - if (uuid != null) { - return creator.apply(uuid); - } else { - return null; - } + return DaoUtil.toEntityId(uuid, creator); } protected static TenantId getTenantId(UUID uuid) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java index 0e8264d4f7..af3932c33a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationRuleEntity.java @@ -69,6 +69,9 @@ public class NotificationRuleEntity extends BaseSqlEntity { @Column(name = ModelConstants.NOTIFICATION_RULE_ADDITIONAL_CONFIG_PROPERTY) private JsonNode additionalConfig; + @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) + private UUID externalId; + public NotificationRuleEntity() {} public NotificationRuleEntity(NotificationRule notificationRule) { @@ -81,6 +84,7 @@ public class NotificationRuleEntity extends BaseSqlEntity { setTriggerConfig(toJson(notificationRule.getTriggerConfig())); setRecipientsConfig(toJson(notificationRule.getRecipientsConfig())); setAdditionalConfig(toJson(notificationRule.getAdditionalConfig())); + setExternalId(getUuid(notificationRule.getExternalId())); } public NotificationRuleEntity(NotificationRuleEntity other) { @@ -93,6 +97,7 @@ public class NotificationRuleEntity extends BaseSqlEntity { this.triggerConfig = other.triggerConfig; this.recipientsConfig = other.recipientsConfig; this.additionalConfig = other.additionalConfig; + this.externalId = other.externalId; } @Override @@ -107,6 +112,7 @@ public class NotificationRuleEntity extends BaseSqlEntity { notificationRule.setTriggerConfig(fromJson(triggerConfig, NotificationRuleTriggerConfig.class)); notificationRule.setRecipientsConfig(fromJson(recipientsConfig, NotificationRuleRecipientsConfig.class)); notificationRule.setAdditionalConfig(fromJson(additionalConfig, NotificationRuleConfig.class)); + notificationRule.setExternalId(getEntityId(externalId, NotificationRuleId::new)); return notificationRule; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java index a4748ae0c2..62dbfc20fd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTargetEntity.java @@ -49,6 +49,9 @@ public class NotificationTargetEntity extends BaseSqlEntity @Column(name = ModelConstants.NOTIFICATION_TARGET_CONFIGURATION_PROPERTY, nullable = false) private JsonNode configuration; + @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) + private UUID externalId; + public NotificationTargetEntity() {} public NotificationTargetEntity(NotificationTarget notificationTarget) { @@ -57,6 +60,7 @@ public class NotificationTargetEntity extends BaseSqlEntity setTenantId(getTenantUuid(notificationTarget.getTenantId())); setName(notificationTarget.getName()); setConfiguration(toJson(notificationTarget.getConfiguration())); + setExternalId(getUuid(notificationTarget.getExternalId())); } @Override @@ -67,6 +71,7 @@ public class NotificationTargetEntity extends BaseSqlEntity notificationTarget.setTenantId(getTenantId(tenantId)); notificationTarget.setName(name); notificationTarget.setConfiguration(fromJson(configuration, NotificationTargetConfig.class)); + notificationTarget.setExternalId(getEntityId(externalId, NotificationTargetId::new)); return notificationTarget; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTemplateEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTemplateEntity.java index 09b0ee7ee9..157217afe2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTemplateEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/NotificationTemplateEntity.java @@ -56,6 +56,9 @@ public class NotificationTemplateEntity extends BaseSqlEntity { +public interface NotificationRuleDao extends Dao, ExportableEntityDao { PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java index 2edbfc580a..912e873bcc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTargetDao.java @@ -22,11 +22,12 @@ import org.thingsboard.server.common.data.notification.targets.NotificationTarge import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; import java.util.List; -public interface NotificationTargetDao extends Dao, TenantEntityDao { +public interface NotificationTargetDao extends Dao, TenantEntityDao, ExportableEntityDao { PageData findByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java index e88f3351cf..a2a896ffa4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java @@ -15,16 +15,18 @@ */ package org.thingsboard.server.dao.notification; +import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; import java.util.List; -public interface NotificationTemplateDao extends Dao { +public interface NotificationTemplateDao extends Dao, ExportableEntityDao { PageData findByTenantIdAndNotificationTypesAndPageLink(TenantId tenantId, List notificationTypes, PageLink pageLink); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 76947c5043..ba4afa7bd0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -754,6 +754,12 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC return ruleChainDao.countByTenantId(tenantId); } + @Override + @Transactional + public void deleteEntity(TenantId tenantId, EntityId id) { + deleteRuleChainById(tenantId, (RuleChainId) id); + } + @Override public EntityType getEntityType() { return EntityType.RULE_CHAIN; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java index 46910f119f..e37ba2a73a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationRuleDao.java @@ -55,9 +55,9 @@ public class JpaNotificationRuleDao extends JpaAbstractDao findInfosByTenantIdAndPageLink(TenantId tenantId, PageLink pageLink) { return DaoUtil.pageToPageData(notificationRuleRepository.findInfosByTenantIdAndSearchText(tenantId.getId(), - Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink, Map.of( - "templateName", "t.name" - )))) + Strings.nullToEmpty(pageLink.getTextSearch()), DaoUtil.toPageable(pageLink, Map.of( + "templateName", "t.name" + )))) .mapData(NotificationRuleInfoEntity::toData); } @@ -82,6 +82,26 @@ public class JpaNotificationRuleDao extends JpaAbstractDao findByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.toPageData(notificationRuleRepository.findByTenantId(tenantId, DaoUtil.toPageable(pageLink))); + } + + @Override + public NotificationRuleId getExternalIdByInternal(NotificationRuleId internalId) { + return DaoUtil.toEntityId(notificationRuleRepository.getExternalIdByInternal(internalId.getId()), NotificationRuleId::new); + } + @Override protected Class getEntityClass() { return NotificationRuleEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java index 4bc6dada62..d976802fd2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTargetDao.java @@ -76,6 +76,26 @@ public class JpaNotificationTargetDao extends JpaAbstractDao findByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.toPageData(notificationTargetRepository.findByTenantId(tenantId, DaoUtil.toPageable(pageLink))); + } + + @Override + public NotificationTargetId getExternalIdByInternal(NotificationTargetId internalId) { + return DaoUtil.toEntityId(notificationTargetRepository.getExternalIdByInternal(internalId.getId()), NotificationTargetId::new); + } + @Override protected Class getEntityClass() { return NotificationTargetEntity.class; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java index a83ca07c28..8a7cecb3d1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java @@ -20,6 +20,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.NotificationTemplateId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.template.NotificationTemplate; @@ -57,6 +58,26 @@ public class JpaNotificationTemplateDao extends JpaAbstractDao findByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.toPageData(notificationTemplateRepository.findByTenantId(tenantId, DaoUtil.toPageable(pageLink))); + } + + @Override + public NotificationTemplateId getExternalIdByInternal(NotificationTemplateId internalId) { + return DaoUtil.toEntityId(notificationTemplateRepository.getExternalIdByInternal(internalId.getId()), NotificationTemplateId::new); + } + @Override protected JpaRepository getRepository() { return notificationTemplateRepository; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java index 32c6d5388e..d47201d02b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationRuleRepository.java @@ -23,6 +23,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.notification.rule.trigger.NotificationRuleTriggerType; +import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.NotificationRuleEntity; import org.thingsboard.server.dao.model.sql.NotificationRuleInfoEntity; @@ -30,7 +31,7 @@ import java.util.List; import java.util.UUID; @Repository -public interface NotificationRuleRepository extends JpaRepository { +public interface NotificationRuleRepository extends JpaRepository, ExportableEntityRepository { String RULE_INFO_QUERY = "SELECT new org.thingsboard.server.dao.model.sql.NotificationRuleInfoEntity(r, t.name, t.configuration) " + "FROM NotificationRuleEntity r INNER JOIN NotificationTemplateEntity t ON r.templateId = t.id"; @@ -59,4 +60,11 @@ public interface NotificationRuleRepository extends JpaRepository findByTenantId(UUID tenantId, Pageable pageable); + + @Query("SELECT externalId FROM NotificationRuleEntity WHERE id = :id") + UUID getExternalIdByInternal(@Param("id") UUID internalId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java index 7ba92db7d9..278fea696e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTargetRepository.java @@ -22,13 +22,14 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.NotificationTargetEntity; import java.util.List; import java.util.UUID; @Repository -public interface NotificationTargetRepository extends JpaRepository { +public interface NotificationTargetRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT t FROM NotificationTargetEntity t WHERE t.tenantId = :tenantId " + "AND (:searchText = '' OR lower(t.name) LIKE lower(concat('%', :searchText, '%')))") @@ -52,4 +53,11 @@ public interface NotificationTargetRepository extends JpaRepository findByTenantId(UUID tenantId, Pageable pageable); + + @Query("SELECT externalId FROM NotificationTargetEntity WHERE id = :id") + UUID getExternalIdByInternal(@Param("id") UUID internalId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java index 1381c43710..73a25b98c3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java @@ -23,13 +23,14 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.notification.NotificationType; +import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.NotificationTemplateEntity; import java.util.List; import java.util.UUID; @Repository -public interface NotificationTemplateRepository extends JpaRepository { +public interface NotificationTemplateRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT t FROM NotificationTemplateEntity t WHERE t.tenantId = :tenantId AND " + "t.notificationType IN :notificationTypes " + @@ -43,4 +44,11 @@ public interface NotificationTemplateRepository extends JpaRepository findByTenantId(UUID tenantId, Pageable pageable); + + @Query("SELECT externalId FROM NotificationTemplateEntity WHERE id = :id") + UUID getExternalIdByInternal(@Param("id") UUID internalId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java index afc4b3661e..63a4ce0a77 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/widget/WidgetsBundleServiceImpl.java @@ -160,6 +160,11 @@ public class WidgetsBundleServiceImpl implements WidgetsBundleService { return Optional.ofNullable(findWidgetsBundleById(tenantId, new WidgetsBundleId(entityId.getId()))); } + @Override + public void deleteEntity(TenantId tenantId, EntityId id) { + deleteWidgetsBundle(tenantId, (WidgetsBundleId) id); + } + @Override public EntityType getEntityType() { return EntityType.WIDGETS_BUNDLE; diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 9e219ed933..444c00ccf2 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -797,7 +797,9 @@ CREATE TABLE IF NOT EXISTS notification_target ( tenant_id UUID NOT NULL, name VARCHAR(255) NOT NULL, configuration VARCHAR(10000) NOT NULL, - CONSTRAINT uq_notification_target_name UNIQUE (tenant_id, name) + external_id UUID, + CONSTRAINT uq_notification_target_name UNIQUE (tenant_id, name), + CONSTRAINT uq_notification_target_external_id UNIQUE (tenant_id, external_id) ); CREATE TABLE IF NOT EXISTS notification_template ( @@ -807,7 +809,9 @@ CREATE TABLE IF NOT EXISTS notification_template ( name VARCHAR(255) NOT NULL, notification_type VARCHAR(50) NOT NULL, configuration VARCHAR(10000000) NOT NULL, - CONSTRAINT uq_notification_template_name UNIQUE (tenant_id, name) + external_id UUID, + CONSTRAINT uq_notification_template_name UNIQUE (tenant_id, name), + CONSTRAINT uq_notification_template_external_id UNIQUE (tenant_id, external_id) ); CREATE TABLE IF NOT EXISTS notification_rule ( @@ -820,7 +824,9 @@ CREATE TABLE IF NOT EXISTS notification_rule ( trigger_config VARCHAR(1000) NOT NULL, recipients_config VARCHAR(10000) NOT NULL, additional_config VARCHAR(255), - CONSTRAINT uq_notification_rule_name UNIQUE (tenant_id, name) + external_id UUID, + CONSTRAINT uq_notification_rule_name UNIQUE (tenant_id, name), + CONSTRAINT uq_notification_rule_external_id UNIQUE (tenant_id, external_id) ); CREATE TABLE IF NOT EXISTS notification_request ( From 97141efd5d8437e5e9ebb1412ebfffe34710cac7 Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Fri, 12 May 2023 14:56:16 +0300 Subject: [PATCH 2/4] UI for notification configs VC --- ui-ngx/src/app/core/http/entity.service.ts | 8 ++++++++ .../vc/entity-types-version-create.component.html | 4 ++-- .../vc/entity-types-version-create.component.ts | 2 ++ .../vc/entity-types-version-load.component.html | 4 ++-- .../vc/entity-types-version-load.component.ts | 8 +++++++- ui-ngx/src/app/shared/models/entity-type.models.ts | 6 ++++++ ui-ngx/src/app/shared/models/notification.models.ts | 9 ++++----- ui-ngx/src/app/shared/models/vc.models.ts | 11 ++++++++++- ui-ngx/src/assets/locale/locale.constant-en_US.json | 8 +++++++- 9 files changed, 48 insertions(+), 12 deletions(-) diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 9c3570310e..5bdbacd1c5 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -422,6 +422,14 @@ export class EntityService { pageLink.sortOrder.property = 'name'; entitiesObservable = this.notificationService.getNotificationTargets(pageLink, subType as NotificationType, config); break; + case EntityType.NOTIFICATION_TEMPLATE: + pageLink.sortOrder.property = 'name'; + entitiesObservable = this.notificationService.getNotificationTemplates(pageLink, null, config); + break; + case EntityType.NOTIFICATION_RULE: + pageLink.sortOrder.property = 'name'; + entitiesObservable = this.notificationService.getNotificationRules(pageLink, config); + break; } return entitiesObservable; } diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html index bd714d9e33..519e87fb97 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html @@ -66,10 +66,10 @@ {{ 'version-control.export-credentials' | translate }} - + {{ 'version-control.export-attributes' | translate }} - + {{ 'version-control.export-relations' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts index b41e51c5c2..ac50042887 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts @@ -31,6 +31,7 @@ import { PageComponent } from '@shared/components/page.component'; import { EntityTypeVersionCreateConfig, exportableEntityTypes, + entityTypesWithNoRelatedData, SyncStrategy, syncStrategyTranslationMap } from '@shared/models/vc.models'; @@ -73,6 +74,7 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements syncStrategyTranslations = syncStrategyTranslationMap; entityTypes = EntityType; + entityTypesWithNoRelatedData = entityTypesWithNoRelatedData loading = true; diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html index 8a3b23ea5e..b508320ad7 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html @@ -66,10 +66,10 @@ {{ 'version-control.load-credentials' | translate }} - + {{ 'version-control.load-attributes' | translate }} - + {{ 'version-control.load-relations' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts index d4f28e3a7b..2fd891df23 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts @@ -28,7 +28,12 @@ import { Validators } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; -import { EntityTypeVersionLoadConfig, exportableEntityTypes, VersionCreationResult } from '@shared/models/vc.models'; +import { + EntityTypeVersionLoadConfig, + exportableEntityTypes, + entityTypesWithNoRelatedData, + VersionCreationResult +} from '@shared/models/vc.models'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; @@ -66,6 +71,7 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On public entityTypesVersionLoadFormGroup: UntypedFormGroup; entityTypes = EntityType; + entityTypesWithNoRelatedData = entityTypesWithNoRelatedData loading = true; diff --git a/ui-ngx/src/app/shared/models/entity-type.models.ts b/ui-ngx/src/app/shared/models/entity-type.models.ts index 062694cb43..1a5f187459 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -368,6 +368,8 @@ export const entityTypeTranslations = new Map, 'label'>{ +export interface NotificationRule extends Omit, 'label'>, ExportableEntity { tenantId: TenantId; templateId: NotificationTemplateId; triggerType: TriggerType; @@ -234,7 +233,7 @@ export interface NonConfirmedNotificationEscalation { targets: Array; } -export interface NotificationTarget extends Omit, 'label'>{ +export interface NotificationTarget extends Omit, 'label'>, ExportableEntity { tenantId: TenantId; configuration: NotificationTargetConfig; } @@ -279,7 +278,7 @@ export const NotificationTargetTypeTranslationMap = new Map, 'label'>{ +export interface NotificationTemplate extends Omit, 'label'>, ExportableEntity { tenantId: TenantId; notificationType: NotificationType; configuration: NotificationTemplateConfig; diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index 155a9c8633..32c86900ba 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -30,9 +30,18 @@ export const exportableEntityTypes: Array = [ EntityType.DEVICE_PROFILE, EntityType.ASSET_PROFILE, EntityType.RULE_CHAIN, - EntityType.WIDGETS_BUNDLE + EntityType.WIDGETS_BUNDLE, + EntityType.NOTIFICATION_TEMPLATE, + EntityType.NOTIFICATION_TARGET, + EntityType.NOTIFICATION_RULE ]; +export const entityTypesWithNoRelatedData: Array = [ + EntityType.NOTIFICATION_TEMPLATE, + EntityType.NOTIFICATION_TARGET, + EntityType.NOTIFICATION_RULE +] + export interface VersionCreateConfig { saveRelations: boolean; saveAttributes: boolean; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index bcaa4a183f..6b3110771c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2060,9 +2060,15 @@ "type-queue": "Queue", "type-notification": "Notification", "type-notification-rule": "Notification rule", + "type-notification-rules": "Notification rules", + "list-of-notification-rules": "{ count, plural, =1 {One notification rule} other {List of # notification rules} }", "type-notification-target": "Notification recipient", + "type-notification-targets": "Notification recipients", + "list-of-notification-targets": "{ count, plural, =1 {One notification recipient} other {List of # notification recipients} }", "type-notification-request": "Notification request", - "type-notification-template": "Notification template" + "type-notification-template": "Notification template", + "type-notification-templates": "Notification templates", + "list-of-notification-templates": "{ count, plural, =1 {One notification template} other {List of # notification templates} }" }, "entity-field": { "created-time": "Created time", From 70450fc8745bd21079d4a43b8c0fd41e3875301d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 19 May 2023 16:07:08 +0300 Subject: [PATCH 3/4] UI: Improved notification version --- ui-ngx/src/app/core/http/entity.service.ts | 2 +- ui-ngx/src/app/core/http/notification.service.ts | 4 ++-- .../vc/entity-types-version-create.component.html | 4 ++-- .../vc/entity-types-version-create.component.ts | 4 ++-- .../vc/entity-types-version-load.component.html | 4 ++-- .../vc/entity-types-version-load.component.ts | 13 ++++++------- ui-ngx/src/app/shared/models/vc.models.ts | 4 ++-- 7 files changed, 17 insertions(+), 18 deletions(-) diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 5bdbacd1c5..ee3e232bba 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -424,7 +424,7 @@ export class EntityService { break; case EntityType.NOTIFICATION_TEMPLATE: pageLink.sortOrder.property = 'name'; - entitiesObservable = this.notificationService.getNotificationTemplates(pageLink, null, config); + entitiesObservable = this.notificationService.getNotificationTemplates(pageLink, subType as NotificationType, config); break; case EntityType.NOTIFICATION_RULE: pageLink.sortOrder.property = 'name'; diff --git a/ui-ngx/src/app/core/http/notification.service.ts b/ui-ngx/src/app/core/http/notification.service.ts index a92007643a..ec64621aeb 100644 --- a/ui-ngx/src/app/core/http/notification.service.ts +++ b/ui-ngx/src/app/core/http/notification.service.ts @@ -35,7 +35,7 @@ import { SlackConversation } from '@shared/models/notification.models'; import { User } from '@shared/models/user.model'; -import { isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; +import { isNotEmptyStr } from '@core/utils'; @Injectable({ providedIn: 'root' @@ -169,7 +169,7 @@ export class NotificationService { public getNotificationTemplates(pageLink: PageLink, notificationTypes?: NotificationType, config?: RequestConfig): Observable> { let url = `/api/notification/templates${pageLink.toQuery()}`; - if (isDefinedAndNotNull(notificationTypes)) { + if (isNotEmptyStr(notificationTypes)) { url += `¬ificationTypes=${notificationTypes}`; } return this.http.get>(url, defaultHttpOptionsFromConfig(config)); diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html index 519e87fb97..ef09a2b382 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html @@ -66,10 +66,10 @@ {{ 'version-control.export-credentials' | translate }} - + {{ 'version-control.export-attributes' | translate }} - + {{ 'version-control.export-relations' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts index ac50042887..b6a394982c 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts @@ -29,9 +29,9 @@ import { } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { + entityTypesWithoutRelatedData, EntityTypeVersionCreateConfig, exportableEntityTypes, - entityTypesWithNoRelatedData, SyncStrategy, syncStrategyTranslationMap } from '@shared/models/vc.models'; @@ -74,7 +74,7 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements syncStrategyTranslations = syncStrategyTranslationMap; entityTypes = EntityType; - entityTypesWithNoRelatedData = entityTypesWithNoRelatedData + entityTypesWithoutRelatedData = entityTypesWithoutRelatedData; loading = true; diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html index b508320ad7..d179855a1a 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html @@ -66,10 +66,10 @@ {{ 'version-control.load-credentials' | translate }} - + {{ 'version-control.load-attributes' | translate }} - + {{ 'version-control.load-relations' | translate }} diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts index 2fd891df23..5405436f35 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts @@ -18,27 +18,26 @@ import { Component, forwardRef, Input, OnInit, Renderer2, ViewContainerRef } fro import { AbstractControl, ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, UntypedFormArray, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, - NG_VALIDATORS, - NG_VALUE_ACCESSOR, Validator, Validators } from '@angular/forms'; import { PageComponent } from '@shared/components/page.component'; import { + entityTypesWithoutRelatedData, EntityTypeVersionLoadConfig, - exportableEntityTypes, - entityTypesWithNoRelatedData, - VersionCreationResult + exportableEntityTypes } from '@shared/models/vc.models'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { TranslateService } from '@ngx-translate/core'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models'; -import { MatCheckbox, MatCheckboxChange } from '@angular/material/checkbox'; +import { MatCheckbox } from '@angular/material/checkbox'; import { TbPopoverService } from '@shared/components/popover.service'; import { RemoveOtherEntitiesConfirmComponent } from '@home/components/vc/remove-other-entities-confirm.component'; @@ -71,7 +70,7 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On public entityTypesVersionLoadFormGroup: UntypedFormGroup; entityTypes = EntityType; - entityTypesWithNoRelatedData = entityTypesWithNoRelatedData + entityTypesWithoutRelatedData = entityTypesWithoutRelatedData; loading = true; diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index 32c86900ba..d1886fb1aa 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -36,11 +36,11 @@ export const exportableEntityTypes: Array = [ EntityType.NOTIFICATION_RULE ]; -export const entityTypesWithNoRelatedData: Array = [ +export const entityTypesWithoutRelatedData: Set = new Set([ EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_TARGET, EntityType.NOTIFICATION_RULE -] +]); export interface VersionCreateConfig { saveRelations: boolean; From 597696eabc7b2e2520c081cffa8da271d14e3fbf Mon Sep 17 00:00:00 2001 From: ViacheslavKlimov Date: Wed, 24 May 2023 13:12:33 +0300 Subject: [PATCH 4/4] Try to fix testNotificationRuleDisabling --- .../server/service/notification/NotificationRuleApiTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 76ecd5cfa2..455b897fcd 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -480,7 +480,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { assertThat(getMyNotifications(false, 100)).size().isZero(); createDevice("Device 1", "default", "111"); - await().atMost(15, TimeUnit.SECONDS) + await().atMost(30, TimeUnit.SECONDS) .untilAsserted(() -> { assertThat(getMyNotifications(false, 100)).size().isEqualTo(1); }); @@ -494,9 +494,10 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { rule.setEnabled(true); saveNotificationRule(rule); + TimeUnit.SECONDS.sleep(2); // for rule update event to reach rules cache createDevice("Device 3", "default", "333"); - await().atMost(15, TimeUnit.SECONDS) + await().atMost(30, TimeUnit.SECONDS) .untilAsserted(() -> { assertThat(getMyNotifications(false, 100)).size().isEqualTo(2); });