Browse Source

feat(iot-hub): implement ALARM_RULE install / update / delete handlers

Replaces the temporary v4.3-upgrade-required rejection with the actual
backend handlers. Alarm rules install as CalculatedField entities with
type=ALARM, mirroring the CALCULATED_FIELD path:

- installAlarmRule parses the alarm-rule JSON, validates type==ALARM,
  saves via tbCalculatedFieldService, and returns
  AlarmRuleInstalledItemDescriptor with calculatedFieldId + entityId.
- updateAlarmRule re-saves the existing CF with new name, type, and
  configuration.
- Delete branch removes the underlying calculated field on installed-item
  delete.
- Checksum dispatch unifies CF and ALARM_RULE through a shared
  calculateCalculatedFieldChecksum(CalculatedFieldId) helper.

Frontend: drops the v4.3 info-dialog interception in IotHubActionsService
(install now actually runs); restores the select-entity branch for
ALARM_RULE in the install dialog so users can pick a parent device.
Removes the now-unused alarm-rule-install-update-required* locale keys.
pull/15548/head
Andrii Shvaika 1 month ago
parent
commit
553bfdd7a5
  1. 64
      application/src/main/java/org/thingsboard/server/service/iot_hub/DefaultIotHubService.java
  2. 13
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts
  3. 2
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts
  4. 2
      ui-ngx/src/assets/locale/locale.constant-en_US.json

64
application/src/main/java/org/thingsboard/server/service/iot_hub/DefaultIotHubService.java

@ -24,6 +24,8 @@ import org.thingsboard.common.util.JacksonUtil;
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.cf.CalculatedFieldType;
import org.thingsboard.server.common.data.id.CalculatedFieldId;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.DashboardId;
import org.thingsboard.server.common.data.id.DeviceId;
@ -32,6 +34,7 @@ import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.IotHubInstalledItemId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.iot_hub.AlarmRuleInstalledItemDescriptor;
import org.thingsboard.server.common.data.iot_hub.CalculatedFieldInstalledItemDescriptor;
import org.thingsboard.server.common.data.iot_hub.DashboardInstalledItemDescriptor;
import org.thingsboard.server.common.data.iot_hub.DeviceInstalledItemDescriptor;
@ -114,8 +117,7 @@ public class DefaultIotHubService implements IotHubService {
case "WIDGET" -> installWidget(user, tenantId, fileData);
case "DASHBOARD" -> installDashboard(user, tenantId, fileData);
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 "ALARM_RULE" -> installAlarmRule(user, tenantId, fileData, data);
case "RULE_CHAIN" -> installRuleChain(tenantId, fileData);
case "DEVICE" -> installDeviceProfile(user, tenantId, fileData);
case "SOLUTION_TEMPLATE" -> installSolution(user, tenantId, fileData, request);
@ -198,6 +200,30 @@ public class DefaultIotHubService implements IotHubService {
return descriptor;
}
private AlarmRuleInstalledItemDescriptor installAlarmRule(SecurityUser user, TenantId tenantId, byte[] fileData, JsonNode data) throws Exception {
CalculatedField alarmRule;
try {
alarmRule = JacksonUtil.fromString(new String(fileData), CalculatedField.class, true);
} catch (Exception e) {
throw new Exception("Failed to parse alarm rule data: " + (e.getCause() != null ? e.getCause().getMessage() : e.getMessage()), e);
}
if (alarmRule.getType() != CalculatedFieldType.ALARM) {
throw new Exception("Expected ALARM-type calculated field for ALARM_RULE install; got: " + alarmRule.getType());
}
alarmRule.setId(null);
alarmRule.setTenantId(tenantId);
if (data != null && data.has("entityId")) {
EntityId entityId = JacksonUtil.treeToValue(data.get("entityId"), EntityId.class);
alarmRule.setEntityId(entityId);
}
CalculatedField saved = tbCalculatedFieldService.save(alarmRule, user);
log.debug("[{}] Alarm rule installed: {}", tenantId, saved.getName());
AlarmRuleInstalledItemDescriptor descriptor = new AlarmRuleInstalledItemDescriptor();
descriptor.setCalculatedFieldId(saved.getId());
descriptor.setEntityId(saved.getEntityId());
return descriptor;
}
private RuleChainInstalledItemDescriptor installRuleChain(TenantId tenantId, byte[] fileData) throws Exception {
JsonNode json = JacksonUtil.toJsonNode(new String(fileData));
@ -301,6 +327,7 @@ public class DefaultIotHubService implements IotHubService {
case "WIDGET" -> updateWidget(user, tenantId, (WidgetInstalledItemDescriptor) descriptor, fileData);
case "DASHBOARD" -> updateDashboard(user, tenantId, (DashboardInstalledItemDescriptor) descriptor, fileData);
case "CALCULATED_FIELD" -> updateCalculatedField(user, tenantId, (CalculatedFieldInstalledItemDescriptor) descriptor, fileData);
case "ALARM_RULE" -> updateAlarmRule(user, tenantId, (AlarmRuleInstalledItemDescriptor) descriptor, fileData);
case "RULE_CHAIN" -> updateRuleChain(tenantId, (RuleChainInstalledItemDescriptor) descriptor, fileData);
case "DEVICE" -> {
DeviceInstalledItemDescriptor dd = (DeviceInstalledItemDescriptor) descriptor;
@ -387,6 +414,26 @@ public class DefaultIotHubService implements IotHubService {
tbCalculatedFieldService.save(existing, user);
}
private void updateAlarmRule(SecurityUser user, TenantId tenantId, AlarmRuleInstalledItemDescriptor descriptor, byte[] fileData) throws Exception {
CalculatedField newCf;
try {
newCf = JacksonUtil.fromString(new String(fileData), CalculatedField.class, true);
} catch (Exception e) {
throw new Exception("Failed to parse alarm rule data: " + (e.getCause() != null ? e.getCause().getMessage() : e.getMessage()), e);
}
if (newCf.getType() != CalculatedFieldType.ALARM) {
throw new Exception("Expected ALARM-type calculated field for ALARM_RULE update; got: " + newCf.getType());
}
CalculatedField existing = calculatedFieldService.findById(tenantId, descriptor.getCalculatedFieldId());
if (existing == null) {
throw new Exception("Alarm rule not found for update");
}
existing.setName(newCf.getName());
existing.setType(newCf.getType());
existing.setConfiguration(newCf.getConfiguration());
tbCalculatedFieldService.save(existing, user);
}
private void updateRuleChain(TenantId tenantId, RuleChainInstalledItemDescriptor descriptor, byte[] fileData) throws Exception {
JsonNode json = JacksonUtil.toJsonNode(new String(fileData));
RuleChainMetaData metadata;
@ -423,15 +470,17 @@ public class DefaultIotHubService implements IotHubService {
} else if (descriptor instanceof DashboardInstalledItemDescriptor dd) {
return calculateDashboardChecksum(tenantId, dd);
} else if (descriptor instanceof CalculatedFieldInstalledItemDescriptor cd) {
return calculateCalculatedFieldChecksum(tenantId, cd);
return calculateCalculatedFieldChecksum(tenantId, cd.getCalculatedFieldId());
} else if (descriptor instanceof AlarmRuleInstalledItemDescriptor ad) {
return calculateCalculatedFieldChecksum(tenantId, ad.getCalculatedFieldId());
} else if (descriptor instanceof RuleChainInstalledItemDescriptor rd) {
return calculateRuleChainChecksum(tenantId, rd);
}
return null;
}
private String calculateCalculatedFieldChecksum(TenantId tenantId, CalculatedFieldInstalledItemDescriptor descriptor) {
CalculatedField cf = calculatedFieldService.findById(tenantId, descriptor.getCalculatedFieldId());
private String calculateCalculatedFieldChecksum(TenantId tenantId, CalculatedFieldId calculatedFieldId) {
CalculatedField cf = calculatedFieldService.findById(tenantId, calculatedFieldId);
if (cf == null) {
return null;
}
@ -590,6 +639,11 @@ public class DefaultIotHubService implements IotHubService {
if (calculatedField != null) {
tbCalculatedFieldService.delete(calculatedField, user);
}
} else if (descriptor instanceof AlarmRuleInstalledItemDescriptor ad) {
CalculatedField alarmRule = calculatedFieldService.findById(tenantId, ad.getCalculatedFieldId());
if (alarmRule != null) {
tbCalculatedFieldService.delete(alarmRule, user);
}
} else if (descriptor instanceof RuleChainInstalledItemDescriptor rd) {
if (rd.getRuleChainId() != null) {
RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, rd.getRuleChainId());

13
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts

@ -18,8 +18,6 @@ import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Observable, of, EMPTY } from 'rxjs';
import { filter, mergeMap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { DialogService } from '@core/services/dialog.service';
import { MpItemVersionView } from '@shared/models/iot-hub/iot-hub-version.models';
import { ItemType } from '@shared/models/iot-hub/iot-hub-item.models';
import { DeviceInstalledItemDescriptor, IotHubInstalledItem } from '@shared/models/iot-hub/iot-hub-installed-item.models';
@ -38,9 +36,7 @@ export class IotHubActionsService {
constructor(
private dialog: MatDialog,
private iotHubApiService: IotHubApiService,
private dialogService: DialogService,
private translate: TranslateService
private iotHubApiService: IotHubApiService
) {}
openItemDetail(item: MpItemVersionView, installedItem?: IotHubInstalledItem, installedItemsCount?: number,
@ -76,13 +72,6 @@ export class IotHubActionsService {
}
installItem(item: MpItemVersionView): Observable<string> {
if (item.type === ItemType.ALARM_RULE) {
this.dialogService.alert(
this.translate.instant('iot-hub.alarm-rule-install-update-required'),
this.translate.instant('iot-hub.alarm-rule-install-update-required-text')
);
return EMPTY;
}
if (item.type === ItemType.DEVICE) {
return this.installDevice(item);
}

2
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-install-dialog.component.ts

@ -75,7 +75,7 @@ export class TbIotHubInstallDialogComponent extends DialogComponent<TbIotHubInst
}
install(): void {
if (this.item.type === ItemType.CALCULATED_FIELD) {
if (this.item.type === ItemType.CALCULATED_FIELD || this.item.type === ItemType.ALARM_RULE) {
this.state = 'select-entity';
return;
}

2
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -4234,8 +4234,6 @@
"popular-solution-templates": "Popular Solution Templates",
"popular-calculated-fields": "Popular Calculated Fields",
"popular-alarm-rules": "Popular Alarm Rules",
"alarm-rule-install-update-required": "Update required",
"alarm-rule-install-update-required-text": "Alarm Rules require ThingsBoard 4.3 or later. Please update your platform instance to install Alarm Rule packages.",
"popular-rule-chains": "Popular Rule Chains",
"popular-devices": "Popular Devices",
"become-creator-text": "Submit your templates to the ThingsBoard IoT Hub to get featured and showcase your solutions to our global community.",

Loading…
Cancel
Save