Browse Source

Notifications to sysadmin on housekeeper task processing failure

pull/10201/head
ViacheslavKlimov 2 years ago
parent
commit
b20b6006e9
  1. 7
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  2. 19
      application/src/main/java/org/thingsboard/server/service/housekeeper/DefaultHousekeeperService.java
  3. 4
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsUnassignTaskProcessor.java
  4. 4
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AttributesDeletionTaskProcessor.java
  5. 4
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EntitiesDeletionTaskProcessor.java
  6. 4
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EntityAlarmsDeletionTaskProcessor.java
  7. 4
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EventsDeletionTaskProcessor.java
  8. 4
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/HousekeeperTaskProcessor.java
  9. 4
      application/src/main/java/org/thingsboard/server/service/housekeeper/processor/TelemetryDeletionTaskProcessor.java
  10. 3
      application/src/main/java/org/thingsboard/server/service/housekeeper/stats/HousekeeperStatsService.java
  11. 37
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  12. 2
      application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java
  13. 53
      application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/TaskProcessingFailureTriggerProcessor.java
  14. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java
  15. 2
      common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsUnassignHousekeeperTask.java
  16. 9
      common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/EntitiesDeletionHousekeeperTask.java
  17. 8
      common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java
  18. 34
      common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java
  19. 3
      common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java
  20. 61
      common/data/src/main/java/org/thingsboard/server/common/data/notification/info/TaskProcessingFailureNotificationInfo.java
  21. 65
      common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/TaskProcessingFailureTrigger.java
  22. 1
      common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java
  23. 3
      common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java
  24. 21
      common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/TaskProcessingFailureNotificationRuleTriggerConfig.java
  25. 28
      dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java
  26. 6
      dao/src/main/java/org/thingsboard/server/dao/housekeeper/HousekeeperService.java
  27. 24
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java
  28. 5
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java
  29. 14
      dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java
  30. 2
      dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java
  31. 5
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java
  32. 5
      dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java
  33. 13
      ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html
  34. 13
      ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts
  35. 3
      ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts
  36. 18
      ui-ngx/src/app/shared/models/notification.models.ts
  37. 46
      ui-ngx/src/assets/help/en_US/notification/task_processing_failure.md
  38. 5
      ui-ngx/src/assets/locale/locale.constant-en_US.json

7
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -110,7 +110,7 @@ public class ThingsboardInstallService {
log.info("Upgrading ThingsBoard from version 3.5.1 to 3.6.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.5.1");
dataUpdateService.updateData("3.5.1");
systemDataLoaderService.updateDefaultNotificationConfigs();
systemDataLoaderService.updateDefaultNotificationConfigs(true);
case "3.6.0":
log.info("Upgrading ThingsBoard from version 3.6.0 to 3.6.1 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.6.0");
@ -126,7 +126,10 @@ public class ThingsboardInstallService {
case "3.6.2":
log.info("Upgrading ThingsBoard from version 3.6.2 to 3.6.3 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.6.2");
systemDataLoaderService.updateDefaultNotificationConfigs();
systemDataLoaderService.updateDefaultNotificationConfigs(true);
case "3.6.3":
log.info("Upgrading ThingsBoard from version 3.6.3 to 3.6.4 ...");
systemDataLoaderService.updateDefaultNotificationConfigs(false);
//TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache
break;
default:

19
application/src/main/java/org/thingsboard/server/service/housekeeper/DefaultHousekeeperService.java

@ -21,10 +21,12 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
import org.thingsboard.server.common.data.notification.rule.trigger.TaskProcessingFailureTrigger;
import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.housekeeper.HousekeeperService;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType;
import org.thingsboard.server.gen.transport.TransportProtos.HousekeeperTaskProto;
import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg;
import org.thingsboard.server.queue.TbQueueConsumer;
@ -60,6 +62,7 @@ public class DefaultHousekeeperService implements HousekeeperService {
private final TbQueueProducer<TbProtoQueueMsg<ToHousekeeperServiceMsg>> producer;
private final HousekeeperReprocessingService reprocessingService;
private final HousekeeperStatsService statsService;
private final NotificationRuleProcessor notificationRuleProcessor;
private final ExecutorService consumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("housekeeper-consumer"));
private final ExecutorService executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("housekeeper-task-processor"));
@ -75,11 +78,13 @@ public class DefaultHousekeeperService implements HousekeeperService {
TbCoreQueueFactory queueFactory,
TbQueueProducerProvider producerProvider,
HousekeeperStatsService statsService,
NotificationRuleProcessor notificationRuleProcessor,
@Lazy List<HousekeeperTaskProcessor<?>> taskProcessors) {
this.consumer = queueFactory.createHousekeeperMsgConsumer();
this.producer = producerProvider.getHousekeeperMsgProducer();
this.reprocessingService = reprocessingService;
this.statsService = statsService;
this.notificationRuleProcessor = notificationRuleProcessor;
this.taskProcessors = taskProcessors.stream().collect(Collectors.toMap(HousekeeperTaskProcessor::getTaskType, p -> p));
}
@ -155,14 +160,14 @@ public class DefaultHousekeeperService implements HousekeeperService {
task.getTaskType(), msg.getTask().getAttempt(), task, error);
reprocessingService.submitForReprocessing(msg, error);
statsService.reportFailure(task.getTaskType(), msg);
notificationRuleProcessor.process(TaskProcessingFailureTrigger.builder()
.task(task)
.error(error)
.attempt(msg.getTask().getAttempt())
.build());
}
}
@Override
public void submitTask(HousekeeperTask task) {
submitTask(task.getEntityId().getId(), task);
}
@Override
public void submitTask(UUID key, HousekeeperTask task) {
log.trace("[{}][{}][{}] Submitting task: {}", task.getTenantId(), task.getEntityId().getEntityType(), task.getEntityId(), task.getTaskType());

4
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AlarmsUnassignTaskProcessor.java

@ -20,8 +20,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType;
import org.thingsboard.server.dao.housekeeper.data.AlarmsUnassignHousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
import org.thingsboard.server.common.data.housekeeper.AlarmsUnassignHousekeeperTask;
import org.thingsboard.server.service.entitiy.alarm.TbAlarmService;
import java.util.List;

4
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/AttributesDeletionTaskProcessor.java

@ -19,8 +19,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
@Component
@RequiredArgsConstructor

4
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EntitiesDeletionTaskProcessor.java

@ -19,8 +19,8 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.thingsboard.server.dao.entity.EntityDaoService;
import org.thingsboard.server.dao.entity.EntityServiceRegistry;
import org.thingsboard.server.dao.housekeeper.data.EntitiesDeletionHousekeeperTask;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType;
import org.thingsboard.server.common.data.housekeeper.EntitiesDeletionHousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
@Component
@RequiredArgsConstructor

4
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EntityAlarmsDeletionTaskProcessor.java

@ -18,8 +18,8 @@ package org.thingsboard.server.service.housekeeper.processor;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
@Component
@RequiredArgsConstructor

4
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/EventsDeletionTaskProcessor.java

@ -18,8 +18,8 @@ package org.thingsboard.server.service.housekeeper.processor;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
@Component
@RequiredArgsConstructor

4
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/HousekeeperTaskProcessor.java

@ -15,8 +15,8 @@
*/
package org.thingsboard.server.service.housekeeper.processor;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
public interface HousekeeperTaskProcessor<T extends HousekeeperTask> {

4
application/src/main/java/org/thingsboard/server/service/housekeeper/processor/TelemetryDeletionTaskProcessor.java

@ -20,8 +20,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery;
import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import java.util.List;

3
application/src/main/java/org/thingsboard/server/service/housekeeper/stats/HousekeeperStatsService.java

@ -23,8 +23,7 @@ import org.thingsboard.server.common.stats.DefaultCounter;
import org.thingsboard.server.common.stats.StatsCounter;
import org.thingsboard.server.common.stats.StatsFactory;
import org.thingsboard.server.common.stats.StatsType;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTaskType;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
import org.thingsboard.server.gen.transport.TransportProtos.ToHousekeeperServiceMsg;
import java.util.ArrayList;

37
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -548,7 +548,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
} else {
ListenableFuture<List<String>> saveFuture = attributesService.save(TenantId.SYS_TENANT_ID, deviceId, DataConstants.SERVER_SCOPE,
Collections.singletonList(new BaseAttributeKvEntry(new BooleanDataEntry(key, value)
, System.currentTimeMillis())));
, System.currentTimeMillis())));
addTsCallback(saveFuture, new TelemetrySaveCallback<>(deviceId, key, value));
}
}
@ -692,23 +692,26 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
@Override
@SneakyThrows
public void updateDefaultNotificationConfigs() {
PageDataIterable<TenantId> tenants = new PageDataIterable<>(tenantService::findTenantsIds, 500);
ExecutorService executor = Executors.newFixedThreadPool(Math.max(Runtime.getRuntime().availableProcessors(), 4));
log.info("Updating default edge failure notification configs for all tenants");
AtomicInteger count = new AtomicInteger();
for (TenantId tenantId : tenants) {
executor.submit(() -> {
notificationSettingsService.updateDefaultNotificationConfigs(tenantId);
int n = count.incrementAndGet();
if (n % 500 == 0) {
log.info("{} tenants processed", n);
}
});
}
executor.shutdown();
executor.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
public void updateDefaultNotificationConfigs(boolean updateTenants) {
log.info("Updating notification configs...");
notificationSettingsService.updateDefaultNotificationConfigs(TenantId.SYS_TENANT_ID);
if (updateTenants) {
PageDataIterable<TenantId> tenants = new PageDataIterable<>(tenantService::findTenantsIds, 500);
ExecutorService executor = Executors.newFixedThreadPool(Math.max(Runtime.getRuntime().availableProcessors(), 4));
AtomicInteger count = new AtomicInteger();
for (TenantId tenantId : tenants) {
executor.submit(() -> {
notificationSettingsService.updateDefaultNotificationConfigs(tenantId);
int n = count.incrementAndGet();
if (n % 500 == 0) {
log.info("{} tenants processed", n);
}
});
}
executor.shutdown();
executor.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
}
}

2
application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java

@ -35,6 +35,6 @@ public interface SystemDataLoaderService {
void createDefaultNotificationConfigs();
void updateDefaultNotificationConfigs();
void updateDefaultNotificationConfigs(boolean updateTenants);
}

53
application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/TaskProcessingFailureTriggerProcessor.java

@ -0,0 +1,53 @@
/**
* Copyright © 2016-2024 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.notification.rule.trigger;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.notification.info.TaskProcessingFailureNotificationInfo;
import org.thingsboard.server.common.data.notification.rule.trigger.TaskProcessingFailureTrigger;
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType;
import org.thingsboard.server.common.data.notification.rule.trigger.config.TaskProcessingFailureNotificationRuleTriggerConfig;
@Service
public class TaskProcessingFailureTriggerProcessor implements NotificationRuleTriggerProcessor<TaskProcessingFailureTrigger, TaskProcessingFailureNotificationRuleTriggerConfig> {
@Override
public boolean matchesFilter(TaskProcessingFailureTrigger trigger, TaskProcessingFailureNotificationRuleTriggerConfig triggerConfig) {
return true;
}
@Override
public TaskProcessingFailureNotificationInfo constructNotificationInfo(TaskProcessingFailureTrigger trigger) {
HousekeeperTask task = trigger.getTask();
return TaskProcessingFailureNotificationInfo.builder()
.tenantId(task.getTenantId())
.entityId(task.getEntityId())
.taskType(task.getTaskType())
.taskDescription(task.getDescription())
.error(StringUtils.truncate(ExceptionUtils.getStackTrace(trigger.getError()), 1024))
.attempt(trigger.getAttempt())
.build();
}
@Override
public NotificationRuleTriggerType getTriggerType() {
return NotificationRuleTriggerType.TASK_PROCESSING_FAILURE;
}
}

2
common/dao-api/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateService.java

@ -32,6 +32,8 @@ public interface NotificationTemplateService {
PageData<NotificationTemplate> findNotificationTemplatesByTenantIdAndNotificationTypes(TenantId tenantId, List<NotificationType> notificationTypes, PageLink pageLink);
int countNotificationTemplatesByTenantIdAndNotificationTypes(TenantId tenantId, List<NotificationType> notificationTypes);
void deleteNotificationTemplateById(TenantId tenantId, NotificationTemplateId id);
void deleteNotificationTemplatesByTenantId(TenantId tenantId);

2
dao/src/main/java/org/thingsboard/server/dao/housekeeper/data/AlarmsUnassignHousekeeperTask.java → common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/AlarmsUnassignHousekeeperTask.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.housekeeper.data;
package org.thingsboard.server.common.data.housekeeper;
import lombok.AccessLevel;
import lombok.Data;

9
dao/src/main/java/org/thingsboard/server/dao/housekeeper/data/EntitiesDeletionHousekeeperTask.java → common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/EntitiesDeletionHousekeeperTask.java

@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.housekeeper.data;
package org.thingsboard.server.common.data.housekeeper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AccessLevel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -34,4 +35,10 @@ public class EntitiesDeletionHousekeeperTask extends HousekeeperTask {
this.entityType = entityType;
}
@JsonIgnore
@Override
public String getDescription() {
return entityType.getNormalName().toLowerCase() + "s deletion";
}
}

8
dao/src/main/java/org/thingsboard/server/dao/housekeeper/data/HousekeeperTask.java → common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTask.java

@ -13,8 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.housekeeper.data;
package org.thingsboard.server.common.data.housekeeper;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
@ -76,4 +77,9 @@ public class HousekeeperTask implements Serializable {
return new EntitiesDeletionHousekeeperTask(tenantId, entityType);
}
@JsonIgnore
public String getDescription() {
return taskType.getDescription() + " for " + entityId.getEntityType().getNormalName().toLowerCase() + " " + entityId.getId();
}
}

34
common/data/src/main/java/org/thingsboard/server/common/data/housekeeper/HousekeeperTaskType.java

@ -0,0 +1,34 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.housekeeper;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Getter
public enum HousekeeperTaskType {
DELETE_ENTITIES("entities deletion"),
DELETE_ATTRIBUTES("attributes deletion"),
DELETE_TELEMETRY("telemetry deletion"),
DELETE_EVENTS("events deletion"),
UNASSIGN_ALARMS("alarms unassigning"),
DELETE_ENTITY_ALARMS("entity alarms deletion");
private final String description;
}

3
common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java

@ -30,6 +30,7 @@ public enum NotificationType {
RULE_NODE,
RATE_LIMITS,
EDGE_CONNECTION,
EDGE_COMMUNICATION_FAILURE
EDGE_COMMUNICATION_FAILURE,
TASK_PROCESSING_FAILURE
}

61
common/data/src/main/java/org/thingsboard/server/common/data/notification/info/TaskProcessingFailureNotificationInfo.java

@ -0,0 +1,61 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.info;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTaskType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.Map;
import static org.thingsboard.server.common.data.util.CollectionsUtil.mapOf;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TaskProcessingFailureNotificationInfo implements RuleOriginatedNotificationInfo {
private TenantId tenantId;
private EntityId entityId;
private HousekeeperTaskType taskType;
private String taskDescription;
private String error;
private int attempt;
@Override
public Map<String, String> getTemplateData() {
return mapOf(
"tenantId", tenantId.toString(),
"entityType", entityId.getEntityType().getNormalName(),
"entityId", entityId.getId().toString(),
"taskType", taskType.getDescription(),
"taskDescription", taskDescription,
"error", error,
"attempt", String.valueOf(attempt)
);
}
@Override
public TenantId getAffectedTenantId() {
return tenantId;
}
}

65
common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/TaskProcessingFailureTrigger.java

@ -0,0 +1,65 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.notification.rule.trigger;
import lombok.Builder;
import lombok.Data;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType;
import java.util.concurrent.TimeUnit;
@Data
@Builder
public class TaskProcessingFailureTrigger implements NotificationRuleTrigger {
private final HousekeeperTask task;
private final int attempt;
private final Throwable error;
@Override
public NotificationRuleTriggerType getType() {
return NotificationRuleTriggerType.TASK_PROCESSING_FAILURE;
}
@Override
public TenantId getTenantId() {
return task.getTenantId();
}
@Override
public EntityId getOriginatorEntityId() {
return task.getEntityId();
}
@Override
public boolean deduplicate() {
return true;
}
@Override
public String getDeduplicationKey() {
return String.join(":", NotificationRuleTrigger.super.getDeduplicationKey(), task.getTaskType().toString());
}
@Override
public long getDefaultDeduplicationDuration() {
return TimeUnit.MINUTES.toMillis(30);
}
}

1
common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerConfig.java

@ -38,6 +38,7 @@ import java.io.Serializable;
@Type(value = RateLimitsNotificationRuleTriggerConfig.class, name = "RATE_LIMITS"),
@Type(value = EdgeConnectionNotificationRuleTriggerConfig.class, name = "EDGE_CONNECTION"),
@Type(value = EdgeCommunicationFailureNotificationRuleTriggerConfig.class, name = "EDGE_COMMUNICATION_FAILURE"),
@Type(value = TaskProcessingFailureNotificationRuleTriggerConfig.class, name = "TASK_PROCESSING_FAILURE")
})
public interface NotificationRuleTriggerConfig extends Serializable {

3
common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/NotificationRuleTriggerType.java

@ -31,7 +31,8 @@ public enum NotificationRuleTriggerType {
NEW_PLATFORM_VERSION(false),
ENTITIES_LIMIT(false),
API_USAGE_LIMIT(false),
RATE_LIMITS(false);
RATE_LIMITS(false),
TASK_PROCESSING_FAILURE(false);
private final boolean tenantLevel;

21
dao/src/main/java/org/thingsboard/server/dao/housekeeper/data/HousekeeperTaskType.java → common/data/src/main/java/org/thingsboard/server/common/data/notification/rule/trigger/config/TaskProcessingFailureNotificationRuleTriggerConfig.java

@ -13,13 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.housekeeper.data;
package org.thingsboard.server.common.data.notification.rule.trigger.config;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class TaskProcessingFailureNotificationRuleTriggerConfig implements NotificationRuleTriggerConfig {
@Override
public NotificationRuleTriggerType getTriggerType() {
return NotificationRuleTriggerType.TASK_PROCESSING_FAILURE;
}
public enum HousekeeperTaskType {
DELETE_ENTITIES,
DELETE_ATTRIBUTES,
DELETE_TELEMETRY, // maybe divide into latest and ts kv history?
DELETE_EVENTS,
UNASSIGN_ALARMS,
DELETE_ENTITY_ALARMS
}

28
dao/src/main/java/org/thingsboard/server/dao/housekeeper/CleanUpService.java

@ -21,12 +21,13 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask;
import org.thingsboard.server.dao.relation.RelationService;
import java.util.Optional;
import java.util.UUID;
@Component
@ -34,7 +35,7 @@ import java.util.UUID;
@Slf4j
public class CleanUpService {
private final HousekeeperService housekeeperService;
private final Optional<HousekeeperService> housekeeperService;
private final RelationService relationService;
@TransactionalEventListener(fallbackExecution = true)
@ -44,24 +45,31 @@ public class CleanUpService {
log.trace("[{}] Handling entity deletion event: {}", tenantId, event);
cleanUpRelatedData(tenantId, entityId);
if (entityId.getEntityType() == EntityType.USER) {
housekeeperService.submitTask(HousekeeperTask.unassignAlarms((User) event.getEntity()));
housekeeperService.ifPresent(housekeeperService -> {
housekeeperService.submitTask(HousekeeperTask.unassignAlarms((User) event.getEntity()));
});
}
}
public void cleanUpRelatedData(TenantId tenantId, EntityId entityId) {
// todo: skipped entities list
relationService.deleteEntityRelations(tenantId, entityId);
housekeeperService.submitTask(HousekeeperTask.deleteAttributes(tenantId, entityId));
housekeeperService.submitTask(HousekeeperTask.deleteTelemetry(tenantId, entityId));
housekeeperService.submitTask(HousekeeperTask.deleteEvents(tenantId, entityId));
housekeeperService.submitTask(HousekeeperTask.deleteEntityAlarms(tenantId, entityId));
housekeeperService.ifPresent(housekeeperService -> {
housekeeperService.submitTask(HousekeeperTask.deleteAttributes(tenantId, entityId));
housekeeperService.submitTask(HousekeeperTask.deleteTelemetry(tenantId, entityId));
housekeeperService.submitTask(HousekeeperTask.deleteEvents(tenantId, entityId));
housekeeperService.submitTask(HousekeeperTask.deleteEntityAlarms(tenantId, entityId));
});
}
public void removeTenantEntities(TenantId tenantId, EntityType... entityTypes) {
UUID tasksKey = UUID.randomUUID(); // so that all tasks are pushed to single partition to be processed synchronously
for (EntityType entityType : entityTypes) {
housekeeperService.submitTask(tasksKey, HousekeeperTask.deleteEntities(tenantId, entityType));
}
// todo: just use tenantId as key in the impl
housekeeperService.ifPresent(housekeeperService -> {
for (EntityType entityType : entityTypes) {
housekeeperService.submitTask(tasksKey, HousekeeperTask.deleteEntities(tenantId, entityType));
}
});
}
}

6
dao/src/main/java/org/thingsboard/server/dao/housekeeper/HousekeeperService.java

@ -15,13 +15,15 @@
*/
package org.thingsboard.server.dao.housekeeper;
import org.thingsboard.server.dao.housekeeper.data.HousekeeperTask;
import org.thingsboard.server.common.data.housekeeper.HousekeeperTask;
import java.util.UUID;
public interface HousekeeperService {
void submitTask(HousekeeperTask task);
default void submitTask(HousekeeperTask task) {
submitTask(task.getEntityId().getId(), task);
}
// tasks with the same key will be pushed to the same partition and thus processed synchronously
void submitTask(UUID key, HousekeeperTask task);

24
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationSettingsService.java

@ -173,17 +173,15 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
defaultNotifications.create(tenantId, DefaultNotifications.entitiesLimitForSysadmin, sysAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.entitiesLimitForTenant, affectedTenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.apiFeatureWarningForSysadmin, sysAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.apiFeatureWarningForTenant, affectedTenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.apiFeatureDisabledForSysadmin, sysAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.apiFeatureDisabledForTenant, affectedTenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimits, affectedTenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.exceededPerEntityRateLimits, affectedTenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.newPlatformVersion, sysAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.taskProcessingFailure, tenantAdmins.getId());
return;
}
@ -206,19 +204,19 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
@Override
public void updateDefaultNotificationConfigs(TenantId tenantId) {
if (tenantId.isSysTenantId()) {
if (notificationTemplateService.findNotificationTemplatesByTenantIdAndNotificationTypes(tenantId,
List.of(NotificationType.RATE_LIMITS), new PageLink(1)).getTotalElements() > 0) {
return;
}
NotificationTarget sysAdmins = notificationTargetService.findNotificationTargetsByTenantIdAndUsersFilterType(tenantId, UsersFilterType.SYSTEM_ADMINISTRATORS).stream()
.findFirst().orElseGet(() -> createTarget(tenantId, "System administrators", new SystemAdministratorsFilter(), "All system administrators"));
NotificationTarget affectedTenantAdmins = notificationTargetService.findNotificationTargetsByTenantIdAndUsersFilterType(tenantId, UsersFilterType.AFFECTED_TENANT_ADMINISTRATORS).stream()
.findFirst().orElseGet(() -> createTarget(tenantId, "Affected tenant's administrators", new AffectedTenantAdministratorsFilter(), ""));
defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimits, affectedTenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.exceededPerEntityRateLimits, affectedTenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId());
if (!isNotificationConfigured(tenantId, NotificationType.RATE_LIMITS)) {
defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimits, affectedTenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.exceededPerEntityRateLimits, affectedTenantAdmins.getId());
defaultNotifications.create(tenantId, DefaultNotifications.exceededRateLimitsForSysadmin, sysAdmins.getId());
}
if (!isNotificationConfigured(tenantId, NotificationType.TASK_PROCESSING_FAILURE)) {
defaultNotifications.create(tenantId, DefaultNotifications.taskProcessingFailure, sysAdmins.getId());
}
} else {
var requiredNotificationTypes = List.of(NotificationType.EDGE_CONNECTION, NotificationType.EDGE_COMMUNICATION_FAILURE);
var existingNotificationTypes = notificationTemplateService.findNotificationTemplatesByTenantIdAndNotificationTypes(
@ -252,6 +250,10 @@ public class DefaultNotificationSettingsService implements NotificationSettingsS
}
}
private boolean isNotificationConfigured(TenantId tenantId, NotificationType... notificationTypes) {
return notificationTemplateService.countNotificationTemplatesByTenantIdAndNotificationTypes(tenantId, List.of(notificationTypes)) > 0;
}
private NotificationTarget createTarget(TenantId tenantId, String name, UsersFilter filter, String description) {
NotificationTarget target = new NotificationTarget();
target.setTenantId(tenantId);

5
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotificationTemplateService.java

@ -69,6 +69,11 @@ public class DefaultNotificationTemplateService extends AbstractEntityService im
return notificationTemplateDao.findByTenantIdAndNotificationTypesAndPageLink(tenantId, notificationTypes, pageLink);
}
@Override
public int countNotificationTemplatesByTenantIdAndNotificationTypes(TenantId tenantId, List<NotificationType> notificationTypes) {
return notificationTemplateDao.countByTenantIdAndNotificationTypes(tenantId, notificationTypes);
}
@Override
public void deleteNotificationTemplateById(TenantId tenantId, NotificationTemplateId id) {
if (notificationRequestDao.existsByTenantIdAndStatusAndTemplateId(tenantId, NotificationRequestStatus.SCHEDULED, id)) {

14
dao/src/main/java/org/thingsboard/server/dao/notification/DefaultNotifications.java

@ -50,6 +50,7 @@ import org.thingsboard.server.common.data.notification.rule.trigger.config.Notif
import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType;
import org.thingsboard.server.common.data.notification.rule.trigger.config.RateLimitsNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.config.RuleEngineComponentLifecycleEventNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.rule.trigger.config.TaskProcessingFailureNotificationRuleTriggerConfig;
import org.thingsboard.server.common.data.notification.template.NotificationTemplate;
import org.thingsboard.server.common.data.notification.template.NotificationTemplateConfig;
import org.thingsboard.server.common.data.notification.template.WebDeliveryMethodNotificationTemplate;
@ -358,6 +359,19 @@ public class DefaultNotifications {
.build())
.build();
public static final DefaultNotification taskProcessingFailure = DefaultNotification.builder()
.name("Task processing failure notification")
.type(NotificationType.TASK_PROCESSING_FAILURE)
.subject("Failed to process ${taskType}")
.text("Failed to process ${taskDescription} for tenant ${tenantId}: ${error}")
.icon("warning").color(YELLOW_COLOR)
.rule(DefaultRule.builder()
.name("Task processing failure")
.triggerConfig(TaskProcessingFailureNotificationRuleTriggerConfig.builder().build())
.description("Send notification to system admins when task processing fails")
.build())
.build();
public static final DefaultNotification jwtSigningKeyIssue = DefaultNotification.builder()
.name("JWT Signing Key issue notification")
.type(NotificationType.GENERAL)

2
dao/src/main/java/org/thingsboard/server/dao/notification/NotificationTemplateDao.java

@ -30,6 +30,8 @@ public interface NotificationTemplateDao extends Dao<NotificationTemplate>, Expo
PageData<NotificationTemplate> findByTenantIdAndNotificationTypesAndPageLink(TenantId tenantId, List<NotificationType> notificationTypes, PageLink pageLink);
int countByTenantIdAndNotificationTypes(TenantId tenantId, List<NotificationType> notificationTypes);
void removeByTenantId(TenantId tenantId);
}

5
dao/src/main/java/org/thingsboard/server/dao/sql/notification/JpaNotificationTemplateDao.java

@ -53,6 +53,11 @@ public class JpaNotificationTemplateDao extends JpaAbstractDao<NotificationTempl
notificationTypes, pageLink.getTextSearch(), DaoUtil.toPageable(pageLink)));
}
@Override
public int countByTenantIdAndNotificationTypes(TenantId tenantId, List<NotificationType> notificationTypes) {
return notificationTemplateRepository.countByTenantIdAndNotificationTypes(tenantId.getId(), notificationTypes);
}
@Override
public void removeByTenantId(TenantId tenantId) {
notificationTemplateRepository.deleteByTenantId(tenantId.getId());

5
dao/src/main/java/org/thingsboard/server/dao/sql/notification/NotificationTemplateRepository.java

@ -42,6 +42,11 @@ public interface NotificationTemplateRepository extends JpaRepository<Notificati
@Param("searchText") String searchText,
Pageable pageable);
@Query("SELECT count(t) FROM NotificationTemplateEntity t WHERE t.tenantId = :tenantId AND " +
"t.notificationType IN :notificationTypes")
int countByTenantIdAndNotificationTypes(@Param("tenantId") UUID tenantId,
@Param("notificationTypes") List<NotificationType> notificationTypes);
@Transactional
@Modifying
@Query("DELETE FROM NotificationTemplateEntity t WHERE t.tenantId = :tenantId")

13
ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.html

@ -581,6 +581,19 @@
</section>
</form>
</mat-step>
<mat-step *ngIf="ruleNotificationForm.get('triggerType').value === triggerType.TASK_PROCESSING_FAILURE"
[stepControl]="taskProcessingFailureTemplateForm">
<ng-template matStepLabel>{{ 'notification.task-processing-failure-trigger-settings' | translate }}</ng-template>
<form [formGroup]="ruleNotificationForm">
<section formGroupName="additionalConfig">
<mat-form-field class="mat-block">
<mat-label translate>notification.description</mat-label>
<input matInput formControlName="description">
</mat-form-field>
</section>
</form>
</mat-step>
</mat-horizontal-stepper>
</div>
<mat-divider></mat-divider>

13
ui-ngx/src/app/modules/home/pages/notification/rule/rule-notification-dialog.component.ts

@ -101,6 +101,7 @@ export class RuleNotificationDialogComponent extends
rateLimitsTemplateForm: FormGroup;
edgeCommunicationFailureTemplateForm: FormGroup;
edgeConnectionTemplateForm: FormGroup;
taskProcessingFailureTemplateForm: FormGroup;
triggerType = TriggerType;
triggerTypes: TriggerType[];
@ -337,6 +338,12 @@ export class RuleNotificationDialogComponent extends
})
});
this.taskProcessingFailureTemplateForm = this.fb.group({
triggerConfig: this.fb.group({
taskTypes: []
})
});
this.triggerTypeFormsMap = new Map<TriggerType, FormGroup>([
[TriggerType.ALARM, this.alarmTemplateForm],
[TriggerType.ALARM_COMMENT, this.alarmCommentTemplateForm],
@ -349,7 +356,8 @@ export class RuleNotificationDialogComponent extends
[TriggerType.NEW_PLATFORM_VERSION, this.newPlatformVersionTemplateForm],
[TriggerType.RATE_LIMITS, this.rateLimitsTemplateForm],
[TriggerType.EDGE_COMMUNICATION_FAILURE, this.edgeCommunicationFailureTemplateForm],
[TriggerType.EDGE_CONNECTION, this.edgeConnectionTemplateForm]
[TriggerType.EDGE_CONNECTION, this.edgeConnectionTemplateForm],
[TriggerType.TASK_PROCESSING_FAILURE, this.taskProcessingFailureTemplateForm]
]);
if (data.isAdd || data.isCopy) {
@ -488,7 +496,8 @@ export class RuleNotificationDialogComponent extends
TriggerType.ENTITIES_LIMIT,
TriggerType.API_USAGE_LIMIT,
TriggerType.NEW_PLATFORM_VERSION,
TriggerType.RATE_LIMITS
TriggerType.RATE_LIMITS,
TriggerType.TASK_PROCESSING_FAILURE
]);
if (this.isSysAdmin()) {

3
ui-ngx/src/app/modules/home/pages/notification/template/template-notification-dialog.component.ts

@ -179,7 +179,8 @@ export class TemplateNotificationDialogComponent
NotificationType.ENTITIES_LIMIT,
NotificationType.API_USAGE_LIMIT,
NotificationType.NEW_PLATFORM_VERSION,
NotificationType.RATE_LIMITS
NotificationType.RATE_LIMITS,
NotificationType.TASK_PROCESSING_FAILURE
]);
if (this.isSysAdmin()) {

18
ui-ngx/src/app/shared/models/notification.models.ts

@ -523,7 +523,8 @@ export enum NotificationType {
RULE_NODE = 'RULE_NODE',
RATE_LIMITS = 'RATE_LIMITS',
EDGE_CONNECTION = 'EDGE_CONNECTION',
EDGE_COMMUNICATION_FAILURE = 'EDGE_COMMUNICATION_FAILURE'
EDGE_COMMUNICATION_FAILURE = 'EDGE_COMMUNICATION_FAILURE',
TASK_PROCESSING_FAILURE = 'TASK_PROCESSING_FAILURE'
}
export const NotificationTypeIcons = new Map<NotificationType, string | null>([
@ -535,6 +536,7 @@ export const NotificationTypeIcons = new Map<NotificationType, string | null>([
[NotificationType.RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT, 'settings_ethernet'],
[NotificationType.ENTITIES_LIMIT, 'data_thresholding'],
[NotificationType.API_USAGE_LIMIT, 'insert_chart'],
[NotificationType.TASK_PROCESSING_FAILURE, 'warning']
]);
export const AlarmSeverityNotificationColors = new Map<AlarmSeverity, string>(
@ -646,7 +648,13 @@ export const NotificationTemplateTypeTranslateMap = new Map<NotificationType, No
name: 'notification.template-type.edge-communication-failure',
helpId: 'notification/edge_communication_failure'
}
]
],
[NotificationType.TASK_PROCESSING_FAILURE,
{
name: 'notification.template-type.task-processing-failure',
helpId: 'notification/task_processing_failure'
}
],
]);
export enum TriggerType {
@ -661,7 +669,8 @@ export enum TriggerType {
NEW_PLATFORM_VERSION = 'NEW_PLATFORM_VERSION',
RATE_LIMITS = 'RATE_LIMITS',
EDGE_CONNECTION = 'EDGE_CONNECTION',
EDGE_COMMUNICATION_FAILURE = 'EDGE_COMMUNICATION_FAILURE'
EDGE_COMMUNICATION_FAILURE = 'EDGE_COMMUNICATION_FAILURE',
TASK_PROCESSING_FAILURE = 'TASK_PROCESSING_FAILURE',
}
export const TriggerTypeTranslationMap = new Map<TriggerType, string>([
@ -676,7 +685,8 @@ export const TriggerTypeTranslationMap = new Map<TriggerType, string>([
[TriggerType.NEW_PLATFORM_VERSION, 'notification.trigger.new-platform-version'],
[TriggerType.RATE_LIMITS, 'notification.trigger.rate-limits'],
[TriggerType.EDGE_CONNECTION, 'notification.trigger.edge-connection'],
[TriggerType.EDGE_COMMUNICATION_FAILURE, 'notification.trigger.edge-communication-failure']
[TriggerType.EDGE_COMMUNICATION_FAILURE, 'notification.trigger.edge-communication-failure'],
[TriggerType.TASK_PROCESSING_FAILURE, 'notification.trigger.task-processing-failure']
]);
export interface NotificationUserSettings {

46
ui-ngx/src/assets/help/en_US/notification/task_processing_failure.md

@ -0,0 +1,46 @@
#### Task processing failure notification templatization
<div class="divider"></div>
<br/>
Notification subject and message fields support templatization.
The list of available templatization parameters depends on the template type.
See the available types and parameters below:
Available template parameters:
* `taskType` - the task type, e.g. 'telemetry deletion';
* `taskDescription` - the task description, e.g. 'telemetry deletion for device c4d93dc0-63a1-11ee-aa6d-f7cbc0a71325';
* `error` - the error stacktrace
* `tenantId` - the tenant id;
* `entityType` - the type of the entity to which the task is related;
* `entityId` - the id of the entity to which the task is related;
* `attempt` - the number of attempts processing the task
Parameter names must be wrapped using `${...}`. For example: `${entityType}`.
You may also modify the value of the parameter with one of the suffixes:
* `upperCase`, for example - `${entityType:upperCase}`
* `lowerCase`, for example - `${entityType:lowerCase}`
* `capitalize`, for example - `${entityType:capitalize}`
<div class="divider"></div>
##### Examples
Let's assume that telemetry deletion failed for some device.
The following template:
```text
Failed to process ${taskType} for ${entityType:lowerCase} ${entityId}
{:copy-code}
```
will be transformed to:
```text
Failed to process telemetry deletion for device c4d93dc0-63a1-11ee-aa6d-f7cbc0a71325
```
<br>
<br>

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

@ -3241,6 +3241,7 @@
"api-usage-trigger-settings": "API usage trigger settings",
"new-platform-version-trigger-settings": "New platform version trigger settings",
"rate-limits-trigger-settings": "Exceeded rate limits trigger settings",
"task-processing-failure-trigger-settings": "Task processing failure trigger settings",
"at-least-one-should-be-selected": "At least one should be selected",
"basic-settings": "Basic settings",
"button-text": "Button text",
@ -3450,7 +3451,8 @@
"new-platform-version": "New platform version",
"rate-limits": "Exceeded rate limits",
"edge-communication-failure": "Edge communication failure",
"edge-connection": "Edge connection"
"edge-connection": "Edge connection",
"task-processing-failure": "Task processing failure"
},
"templates": "Templates",
"notification-templates": "Notifications / Templates",
@ -3473,6 +3475,7 @@
"rate-limits": "Exceeded rate limits",
"edge-connection": "Edge connection",
"edge-communication-failure": "Edge communication failure",
"task-processing-failure": "Task processing failure",
"trigger": "Trigger",
"trigger-required": "Trigger is required"
},

Loading…
Cancel
Save