diff --git a/application/src/main/java/org/thingsboard/server/service/iot_hub/DefaultIotHubService.java b/application/src/main/java/org/thingsboard/server/service/iot_hub/DefaultIotHubService.java index 862be2034f..5c08b4f12b 100644 --- a/application/src/main/java/org/thingsboard/server/service/iot_hub/DefaultIotHubService.java +++ b/application/src/main/java/org/thingsboard/server/service/iot_hub/DefaultIotHubService.java @@ -21,10 +21,12 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.cf.CalculatedField; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.AssetProfileId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -47,6 +49,7 @@ import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; +import org.thingsboard.server.dao.asset.AssetProfileService; import org.thingsboard.server.dao.cf.CalculatedFieldService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceProfileService; @@ -55,6 +58,7 @@ import org.thingsboard.server.dao.iot_hub.IotHubInstalledItemService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.widget.WidgetTypeService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.asset.profile.TbAssetProfileService; import org.thingsboard.server.service.entitiy.cf.TbCalculatedFieldService; import org.thingsboard.server.service.entitiy.dashboard.TbDashboardService; import org.thingsboard.server.service.entitiy.device.TbDeviceService; @@ -84,6 +88,8 @@ public class DefaultIotHubService implements IotHubService { private final RuleChainService ruleChainService; private final TbRuleChainService tbRuleChainService; private final TbDeviceProfileService tbDeviceProfileService; + private final AssetProfileService assetProfileService; + private final TbAssetProfileService tbAssetProfileService; private final IotHubInstalledItemService iotHubInstalledItemService; private final WidgetTypeService widgetTypeService; private final DashboardService dashboardService; @@ -115,7 +121,7 @@ public class DefaultIotHubService implements IotHubService { case "CALCULATED_FIELD" -> installCalculatedField(user, tenantId, fileData, data); case "ALARM_RULE" -> throw new IllegalArgumentException( "Alarm Rules require ThingsBoard 4.3 or later. Please update your platform instance to install Alarm Rule packages."); - case "RULE_CHAIN" -> installRuleChain(tenantId, fileData); + case "RULE_CHAIN" -> installRuleChain(user, tenantId, fileData, data); case "DEVICE" -> installDeviceProfile(user, tenantId, fileData); case "SOLUTION_TEMPLATE" -> installSolution(user, tenantId, fileData, request); default -> throw new IllegalArgumentException("Unsupported IoT Hub item type: " + itemType); @@ -194,7 +200,8 @@ public class DefaultIotHubService implements IotHubService { return descriptor; } - private RuleChainInstalledItemDescriptor installRuleChain(TenantId tenantId, byte[] fileData) throws Exception { + private RuleChainInstalledItemDescriptor installRuleChain( + SecurityUser user, TenantId tenantId, byte[] fileData, JsonNode data) throws Exception { JsonNode json = JacksonUtil.toJsonNode(new String(fileData)); RuleChain ruleChain; @@ -220,11 +227,52 @@ public class DefaultIotHubService implements IotHubService { ruleChainService.saveRuleChainMetaData(tenantId, metadata, tbRuleChainService::updateRuleNodeConfiguration); log.debug("[{}] Rule chain installed: {}", tenantId, savedRuleChain.getName()); + RuleChainInstalledItemDescriptor descriptor = new RuleChainInstalledItemDescriptor(); descriptor.setRuleChainId(savedRuleChain.getId()); + + if (data != null && data.has("entityId") && !data.get("entityId").isNull()) { + EntityId entityId = JacksonUtil.treeToValue(data.get("entityId"), EntityId.class); + setAsDefaultRuleChain(user, tenantId, entityId, savedRuleChain.getId()); + descriptor.setTargetProfileId(entityId); + } + return descriptor; } + private void setAsDefaultRuleChain(SecurityUser user, TenantId tenantId, + EntityId entityId, RuleChainId ruleChainId) throws Exception { + switch (entityId.getEntityType()) { + case DEVICE_PROFILE -> { + DeviceProfile profile = deviceProfileService.findDeviceProfileById( + tenantId, (DeviceProfileId) entityId); + if (profile == null) { + throw new IllegalArgumentException( + "Device profile not found: " + entityId.getId()); + } + profile.setDefaultRuleChainId(ruleChainId); + tbDeviceProfileService.save(profile, user); + log.debug("[{}] Set rule chain {} as default on device profile {}", + tenantId, ruleChainId.getId(), entityId.getId()); + } + case ASSET_PROFILE -> { + AssetProfile profile = assetProfileService.findAssetProfileById( + tenantId, (AssetProfileId) entityId); + if (profile == null) { + throw new IllegalArgumentException( + "Asset profile not found: " + entityId.getId()); + } + profile.setDefaultRuleChainId(ruleChainId); + tbAssetProfileService.save(profile, user); + log.debug("[{}] Set rule chain {} as default on asset profile {}", + tenantId, ruleChainId.getId(), entityId.getId()); + } + default -> throw new IllegalArgumentException( + "Rule chain can only be set as default on device or asset profile, got: " + + entityId.getEntityType()); + } + } + private DeviceInstalledItemDescriptor installDeviceProfile(SecurityUser user, TenantId tenantId, byte[] fileData) throws Exception { DeviceProfile deviceProfile; try { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/iot_hub/RuleChainInstalledItemDescriptor.java b/common/data/src/main/java/org/thingsboard/server/common/data/iot_hub/RuleChainInstalledItemDescriptor.java index f684607759..c71a4bc6f6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/iot_hub/RuleChainInstalledItemDescriptor.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/iot_hub/RuleChainInstalledItemDescriptor.java @@ -16,11 +16,13 @@ package org.thingsboard.server.common.data.iot_hub; import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.RuleChainId; @Data public class RuleChainInstalledItemDescriptor implements IotHubInstalledItemDescriptor { private RuleChainId ruleChainId; + private EntityId targetProfileId; } diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html index c55d3c4655..19a43fcb30 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.html @@ -19,20 +19,35 @@ @switch (state) { @case ('confirm') {
{{ 'iot-hub.install-desc' | translate }}
+ @if (item.type === ItemType.RULE_CHAIN) { +{{ 'iot-hub.rule-chain-install-desc' | translate }}
+ } @else { +{{ 'iot-hub.install-desc' | translate }}
+ } } @case ('select-entity') {{{ 'iot-hub.select-entity-for-cf' | translate:{ name: item.name } }}
+ @if (activeSelectEntityConfig?.promptKey) { + + }{{ 'iot-hub.install-desc' | translate }}
@@ -58,11 +73,37 @@ @switch (state) { @case ('confirm') { - + @if (item.type === ItemType.RULE_CHAIN) { + + + } @else { + + } } @case ('select-entity') { - + @if (item.type === ItemType.RULE_CHAIN) { + + + } @else { + + } + } + @case ('confirm-overwrite') { + + } @case ('installing') { diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.scss b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.scss index bf04778bcd..6fe3d0bfa2 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.scss @@ -14,6 +14,14 @@ * limitations under the License. */ +@import "../../../../../scss/constants"; + +tb-entity-select { + @media #{$mat-gt-sm} { + min-width: 512px; + } +} + .tb-iot-hub-install-title { font-size: 20px; font-weight: 600; diff --git a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts index 83a7d96f44..be37e6e1bb 100644 --- a/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts @@ -29,12 +29,37 @@ import { EntityType } from '@shared/models/entity-type.models'; import { EntityId } from '@shared/models/id/entity-id'; import { resolveEntityDetailsUrl } from './iot-hub-components.models'; import { SolutionInstallDialogComponent } from '@home/components/iot-hub/solution-install-dialog.component'; +import { Observable, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { DeviceProfileService } from '@core/http/device-profile.service'; +import { AssetProfileService } from '@core/http/asset-profile.service'; +import { RuleChainService } from '@core/http/rule-chain.service'; +import { RuleChainId } from '@shared/models/id/rule-chain-id'; + +interface SelectEntityConfig { + allowed: EntityType[]; + defaultType: EntityType; + required: boolean; + promptKey?: string; +} + +interface PendingOverwrite { + entityId: EntityId; + profileName: string; + existingRuleChainName: string; +} export interface IotHubInstallDialogData { item: MpItemVersionView; } -export type InstallState = 'select-entity' | 'confirm' | 'installing' | 'success' | 'error'; +export type InstallState = + | 'select-entity' + | 'confirm-overwrite' + | 'confirm' + | 'installing' + | 'success' + | 'error'; @Component({ selector: 'tb-iot-hub-install-dialog', @@ -53,8 +78,26 @@ export class TbIotHubInstallDialogComponent extends DialogComponent