Notification rule node; refactoring
This commit is contained in:
		
							parent
							
								
									4e8e33ca1c
								
							
						
					
					
						commit
						cd50eabd40
					
				@ -193,7 +193,7 @@ public class NotificationController extends BaseController {
 | 
			
		||||
        notificationRequest.setStatus(null);
 | 
			
		||||
        notificationRequest.setStats(null);
 | 
			
		||||
 | 
			
		||||
        return doSaveAndLog(EntityType.NOTIFICATION_REQUEST, notificationRequest, notificationCenter::processNotificationRequest);
 | 
			
		||||
        return doSaveAndLog(EntityType.NOTIFICATION_REQUEST, notificationRequest, (tenantId, request) -> notificationCenter.processNotificationRequest(tenantId, request, null));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/notification/request/preview")
 | 
			
		||||
 | 
			
		||||
@ -82,6 +82,7 @@ import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
@ -105,7 +106,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
    private Map<NotificationDeliveryMethod, NotificationChannel> channels;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest request) {
 | 
			
		||||
    public NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest request, Consumer<NotificationRequestStats> callback) {
 | 
			
		||||
        if (!rateLimitService.checkRateLimit(tenantId, LimitedApi.NOTIFICATION_REQUEST)) {
 | 
			
		||||
            throw new TbRateLimitsException(EntityType.TENANT);
 | 
			
		||||
        }
 | 
			
		||||
@ -173,6 +174,14 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    log.error("[{}] Failed to update stats for notification request", requestId, e);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (callback != null) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        callback.accept(stats);
 | 
			
		||||
                    } catch (Exception e) {
 | 
			
		||||
                        log.error("Failed to process callback for notification request {}", requestId, e);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }, dbCallbackExecutorService);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -110,7 +110,7 @@ public class DefaultNotificationSchedulerService extends AbstractPartitionBasedS
 | 
			
		||||
 | 
			
		||||
            notificationExecutor.executeAsync(() -> {
 | 
			
		||||
                try {
 | 
			
		||||
                    notificationCenter.processNotificationRequest(tenantId, notificationRequest);
 | 
			
		||||
                    notificationCenter.processNotificationRequest(tenantId, notificationRequest, null);
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    log.error("Failed to process scheduled notification request {}", notificationRequest.getId(), e);
 | 
			
		||||
                    NotificationRequestStats stats = new NotificationRequestStats();
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,6 @@ import com.google.common.base.Strings;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import org.apache.commons.collections4.MapUtils;
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
 | 
			
		||||
@ -32,12 +31,12 @@ import org.thingsboard.server.common.data.notification.template.HasSubject;
 | 
			
		||||
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;
 | 
			
		||||
import org.thingsboard.server.common.data.util.TemplateUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.EnumMap;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
 | 
			
		||||
 | 
			
		||||
@ -58,7 +57,6 @@ public class NotificationProcessingContext {
 | 
			
		||||
    @Getter
 | 
			
		||||
    private final NotificationRequestStats stats;
 | 
			
		||||
 | 
			
		||||
    private static final Pattern TEMPLATE_PARAM_PATTERN = Pattern.compile("\\$\\{([a-zA-Z]+)(:[a-zA-Z]+)?}");
 | 
			
		||||
 | 
			
		||||
    @Builder
 | 
			
		||||
    public NotificationProcessingContext(TenantId tenantId, NotificationRequest request, NotificationTemplate template, NotificationSettings settings) {
 | 
			
		||||
@ -109,47 +107,25 @@ public class NotificationProcessingContext {
 | 
			
		||||
        if (templateContext.isEmpty()) return template;
 | 
			
		||||
 | 
			
		||||
        template = (T) template.copy();
 | 
			
		||||
        template.setBody(processTemplate(template.getBody(), templateContext));
 | 
			
		||||
        template.setBody(TemplateUtils.processTemplate(template.getBody(), templateContext));
 | 
			
		||||
        if (template instanceof HasSubject) {
 | 
			
		||||
            String subject = ((HasSubject) template).getSubject();
 | 
			
		||||
            ((HasSubject) template).setSubject(processTemplate(subject, templateContext));
 | 
			
		||||
            ((HasSubject) template).setSubject(TemplateUtils.processTemplate(subject, templateContext));
 | 
			
		||||
        }
 | 
			
		||||
        if (template instanceof WebDeliveryMethodNotificationTemplate) {
 | 
			
		||||
            WebDeliveryMethodNotificationTemplate webNotificationTemplate = (WebDeliveryMethodNotificationTemplate) template;
 | 
			
		||||
            String buttonText = webNotificationTemplate.getButtonText();
 | 
			
		||||
            if (isNotEmpty(buttonText)) {
 | 
			
		||||
                webNotificationTemplate.setButtonText(processTemplate(buttonText, templateContext));
 | 
			
		||||
                webNotificationTemplate.setButtonText(TemplateUtils.processTemplate(buttonText, templateContext));
 | 
			
		||||
            }
 | 
			
		||||
            String buttonLink = webNotificationTemplate.getButtonLink();
 | 
			
		||||
            if (isNotEmpty(buttonLink)) {
 | 
			
		||||
                webNotificationTemplate.setButtonLink(processTemplate(buttonLink, templateContext));
 | 
			
		||||
                webNotificationTemplate.setButtonLink(TemplateUtils.processTemplate(buttonLink, templateContext));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return template;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String processTemplate(String template, Map<String, String> context) {
 | 
			
		||||
        return TEMPLATE_PARAM_PATTERN.matcher(template).replaceAll(matchResult -> {
 | 
			
		||||
            String key = matchResult.group(1);
 | 
			
		||||
            if (!context.containsKey(key)) {
 | 
			
		||||
                return "\\" + matchResult.group(); // adding escape char due to special meaning of '$' to matcher
 | 
			
		||||
            }
 | 
			
		||||
            String value = Strings.nullToEmpty(context.get(key));
 | 
			
		||||
            String function = matchResult.group(2);
 | 
			
		||||
            if (function != null) {
 | 
			
		||||
                switch (function) {
 | 
			
		||||
                    case ":upperCase":
 | 
			
		||||
                        return value.toUpperCase();
 | 
			
		||||
                    case ":lowerCase":
 | 
			
		||||
                        return value.toLowerCase();
 | 
			
		||||
                    case ":capitalize":
 | 
			
		||||
                        return StringUtils.capitalize(value.toLowerCase());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return value;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Map<String, String> createTemplateContextForRecipient(NotificationRecipient recipient) {
 | 
			
		||||
        return Map.of(
 | 
			
		||||
                "recipientEmail", Strings.nullToEmpty(recipient.getEmail()),
 | 
			
		||||
 | 
			
		||||
@ -154,7 +154,7 @@ public class DefaultNotificationRuleProcessingService implements NotificationRul
 | 
			
		||||
        notificationExecutor.submit(() -> {
 | 
			
		||||
            try {
 | 
			
		||||
                log.debug("Submitting notification request for rule '{}' with delay of {} sec to targets {}", rule.getName(), delayInSec, targets);
 | 
			
		||||
                notificationCenter.processNotificationRequest(rule.getTenantId(), notificationRequest);
 | 
			
		||||
                notificationCenter.processNotificationRequest(rule.getTenantId(), notificationRequest, null);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                log.error("Failed to process notification request for tenant {} for rule {}", rule.getTenantId(), rule.getId(), e);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -24,7 +24,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.thingsboard.rule.engine.api.NotificationCenter;
 | 
			
		||||
import org.thingsboard.server.common.data.User;
 | 
			
		||||
import org.thingsboard.server.common.data.id.NotificationTargetId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.UserId;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.Notification;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
 | 
			
		||||
@ -333,8 +332,8 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
        target1Config.setUsersFilter(userListFilter);
 | 
			
		||||
        target1.setConfiguration(target1Config);
 | 
			
		||||
        target1 = saveNotificationTarget(target1);
 | 
			
		||||
        List<UserId> recipients = new ArrayList<>();
 | 
			
		||||
        recipients.add(tenantAdminUserId);
 | 
			
		||||
        List<String> recipients = new ArrayList<>();
 | 
			
		||||
        recipients.add(TENANT_ADMIN_EMAIL);
 | 
			
		||||
 | 
			
		||||
        createDifferentCustomer();
 | 
			
		||||
        loginTenantAdmin();
 | 
			
		||||
@ -346,7 +345,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
            customerUser.setCustomerId(differentCustomerId);
 | 
			
		||||
            customerUser.setEmail("other-customer-" + i + "@thingsboard.org");
 | 
			
		||||
            customerUser = createUser(customerUser, "12345678");
 | 
			
		||||
            recipients.add(customerUser.getId());
 | 
			
		||||
            recipients.add(customerUser.getEmail());
 | 
			
		||||
        }
 | 
			
		||||
        NotificationTarget target2 = new NotificationTarget();
 | 
			
		||||
        target2.setName("Other customer users");
 | 
			
		||||
@ -402,7 +401,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
        assertThat(preview.getRecipientsCountByTarget().get(target1.getName())).isEqualTo(1);
 | 
			
		||||
        assertThat(preview.getRecipientsCountByTarget().get(target2.getName())).isEqualTo(customerUsersCount);
 | 
			
		||||
        assertThat(preview.getTotalRecipientsCount()).isEqualTo(1 + customerUsersCount);
 | 
			
		||||
        assertThat(preview.getRecipientsPreview()).extracting(User::getId).containsAll(recipients);
 | 
			
		||||
        assertThat(preview.getRecipientsPreview()).containsAll(recipients);
 | 
			
		||||
 | 
			
		||||
        Map<NotificationDeliveryMethod, DeliveryMethodNotificationTemplate> processedTemplates = preview.getProcessedTemplates();
 | 
			
		||||
        assertThat(processedTemplates.get(NotificationDeliveryMethod.WEB)).asInstanceOf(type(WebDeliveryMethodNotificationTemplate.class))
 | 
			
		||||
@ -485,7 +484,7 @@ public class NotificationApiTest extends AbstractNotificationApiTest {
 | 
			
		||||
        NotificationTemplateConfig config = new NotificationTemplateConfig();
 | 
			
		||||
        SlackDeliveryMethodNotificationTemplate slackNotificationTemplate = new SlackDeliveryMethodNotificationTemplate();
 | 
			
		||||
        slackNotificationTemplate.setEnabled(true);
 | 
			
		||||
        slackNotificationTemplate.setBody("To Slack :)  ${recipientEmail}");
 | 
			
		||||
        slackNotificationTemplate.setBody("To Slack :)");
 | 
			
		||||
        config.setDeliveryMethodsTemplates(Map.of(
 | 
			
		||||
                NotificationDeliveryMethod.SLACK, slackNotificationTemplate
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ public enum NotificationType {
 | 
			
		||||
    ALARM_ASSIGNMENT,
 | 
			
		||||
    NEW_PLATFORM_VERSION,
 | 
			
		||||
    ENTITIES_LIMIT,
 | 
			
		||||
    API_USAGE_LIMIT
 | 
			
		||||
    API_USAGE_LIMIT,
 | 
			
		||||
    RULE_ENGINE
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -33,10 +33,13 @@ public class RuleEngineOriginatedNotificationInfo implements NotificationInfo {
 | 
			
		||||
    private EntityId msgOriginator;
 | 
			
		||||
    private String msgType;
 | 
			
		||||
    private Map<String, String> msgMetadata;
 | 
			
		||||
    private Map<String, String> msgData;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Map<String, String> getTemplateData() {
 | 
			
		||||
        Map<String, String> templateData = new HashMap<>(msgMetadata);
 | 
			
		||||
        Map<String, String> templateData = new HashMap<>();
 | 
			
		||||
        templateData.putAll(msgMetadata);
 | 
			
		||||
        templateData.putAll(msgData);
 | 
			
		||||
        templateData.put("originatorType", msgOriginator.getEntityType().getNormalName());
 | 
			
		||||
        templateData.put("originatorId", msgOriginator.getId().toString());
 | 
			
		||||
        templateData.put("msgType", msgType);
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,56 @@
 | 
			
		||||
/**
 | 
			
		||||
 * 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.common.data.util;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.lang3.StringUtils;
 | 
			
		||||
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.function.UnaryOperator;
 | 
			
		||||
import java.util.regex.Pattern;
 | 
			
		||||
 | 
			
		||||
import static com.google.common.base.Strings.nullToEmpty;
 | 
			
		||||
import static org.apache.commons.lang3.StringUtils.removeStart;
 | 
			
		||||
 | 
			
		||||
public class TemplateUtils {
 | 
			
		||||
 | 
			
		||||
    private static final Pattern TEMPLATE_PARAM_PATTERN = Pattern.compile("\\$\\{(.+?)(:[a-zA-Z]+)?}");
 | 
			
		||||
 | 
			
		||||
    private static final Map<String, UnaryOperator<String>> FUNCTIONS = Map.of(
 | 
			
		||||
            "upperCase", String::toUpperCase,
 | 
			
		||||
            "lowerCase", String::toLowerCase,
 | 
			
		||||
            "capitalize", StringUtils::capitalize
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    private TemplateUtils() {}
 | 
			
		||||
 | 
			
		||||
    public static String processTemplate(String template, Map<String, String> context) {
 | 
			
		||||
        return TEMPLATE_PARAM_PATTERN.matcher(template).replaceAll(matchResult -> {
 | 
			
		||||
            String key = matchResult.group(1);
 | 
			
		||||
            if (!context.containsKey(key)) {
 | 
			
		||||
                return "\\" + matchResult.group();
 | 
			
		||||
            }
 | 
			
		||||
            String value = nullToEmpty(context.get(key));
 | 
			
		||||
            String function = removeStart(matchResult.group(2), ":");
 | 
			
		||||
            if (function != null) {
 | 
			
		||||
                if (FUNCTIONS.containsKey(function)) {
 | 
			
		||||
                    value = FUNCTIONS.get(function).apply(value);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return value;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -32,7 +32,10 @@ import org.thingsboard.server.common.data.kv.KvEntry;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.function.UnaryOperator;
 | 
			
		||||
@ -237,6 +240,25 @@ public class JacksonUtil {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Map<String, String> toFlatMap(JsonNode node) {
 | 
			
		||||
        HashMap<String, String> map = new HashMap<>();
 | 
			
		||||
        toFlatMap(node, "", map);
 | 
			
		||||
        return map;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void toFlatMap(JsonNode node, String currentPath, Map<String, String> map) {
 | 
			
		||||
        if (node.isObject()) {
 | 
			
		||||
            Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
 | 
			
		||||
            currentPath = currentPath.isEmpty() ? "" : currentPath + ".";
 | 
			
		||||
            while (fields.hasNext()) {
 | 
			
		||||
                Map.Entry<String, JsonNode> entry = fields.next();
 | 
			
		||||
                toFlatMap(entry.getValue(), currentPath + entry.getKey(), map);
 | 
			
		||||
            }
 | 
			
		||||
        } else if (node.isValueNode()) {
 | 
			
		||||
            map.put(currentPath, node.asText());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void addKvEntry(ObjectNode entityNode, KvEntry kvEntry) {
 | 
			
		||||
        addKvEntry(entityNode, kvEntry, kvEntry.getKey());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -21,12 +21,14 @@ import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.UserId;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationDeliveryMethod;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequestStats;
 | 
			
		||||
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.function.Consumer;
 | 
			
		||||
 | 
			
		||||
public interface NotificationCenter {
 | 
			
		||||
 | 
			
		||||
    NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest);
 | 
			
		||||
    NotificationRequest processNotificationRequest(TenantId tenantId, NotificationRequest notificationRequest, Consumer<NotificationRequestStats> callback);
 | 
			
		||||
 | 
			
		||||
    void deleteNotificationRequest(TenantId tenantId, NotificationRequestId notificationRequestId);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,12 +16,14 @@
 | 
			
		||||
package org.thingsboard.rule.engine.notification;
 | 
			
		||||
 | 
			
		||||
import org.thingsboard.common.util.DonAsynchron;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.rule.engine.api.RuleNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.id.NotificationTemplateId;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequest;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.NotificationRequestConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.notification.info.RuleEngineOriginatedNotificationInfo;
 | 
			
		||||
@ -36,7 +38,7 @@ import java.util.concurrent.ExecutionException;
 | 
			
		||||
        name = "send notification",
 | 
			
		||||
        configClazz = TbNotificationNodeConfiguration.class,
 | 
			
		||||
        nodeDescription = "Sends notification to targets using the template",
 | 
			
		||||
        nodeDetails = "Will send notification to the specified targets",
 | 
			
		||||
        nodeDetails = "Will send notification to the specified targets using the template",
 | 
			
		||||
        uiResources = {"static/rulenode/rulenode-core-config.js"}
 | 
			
		||||
)
 | 
			
		||||
public class TbNotificationNode implements TbNode {
 | 
			
		||||
@ -53,29 +55,28 @@ public class TbNotificationNode implements TbNode {
 | 
			
		||||
        RuleEngineOriginatedNotificationInfo notificationInfo = RuleEngineOriginatedNotificationInfo.builder()
 | 
			
		||||
                .msgOriginator(msg.getOriginator())
 | 
			
		||||
                .msgMetadata(msg.getMetaData().getData())
 | 
			
		||||
                .msgData(JacksonUtil.toFlatMap(JacksonUtil.toJsonNode(msg.getData())))
 | 
			
		||||
                .msgType(msg.getType())
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        NotificationRequest notificationRequest = NotificationRequest.builder()
 | 
			
		||||
                .tenantId(ctx.getTenantId())
 | 
			
		||||
                .targets(config.getTargets())
 | 
			
		||||
                .templateId(config.getTemplateId())
 | 
			
		||||
                .templateId(new NotificationTemplateId(config.getTemplateId()))
 | 
			
		||||
                .info(notificationInfo)
 | 
			
		||||
                .additionalConfig(new NotificationRequestConfig())
 | 
			
		||||
                .originatorEntityId(ctx.getSelf().getRuleChainId())
 | 
			
		||||
                .build();
 | 
			
		||||
 | 
			
		||||
        DonAsynchron.withCallback(ctx.getNotificationExecutor().executeAsync(() -> {
 | 
			
		||||
                    return ctx.getNotificationCenter().processNotificationRequest(ctx.getTenantId(), notificationRequest);
 | 
			
		||||
                    return ctx.getNotificationCenter().processNotificationRequest(ctx.getTenantId(), notificationRequest, stats -> {
 | 
			
		||||
                        TbMsgMetaData metaData = msg.getMetaData().copy();
 | 
			
		||||
                        metaData.putValue("notificationRequestResult", JacksonUtil.toString(stats));
 | 
			
		||||
                        ctx.tellSuccess(TbMsg.transformMsg(msg, metaData));
 | 
			
		||||
                    });
 | 
			
		||||
                }),
 | 
			
		||||
                r -> {
 | 
			
		||||
                    TbMsgMetaData msgMetaData = msg.getMetaData().copy();
 | 
			
		||||
                    msgMetaData.putValue("notificationRequestId", r.getUuidId().toString());
 | 
			
		||||
                    ctx.tellSuccess(TbMsg.transformMsg(msg, msgMetaData));
 | 
			
		||||
                },
 | 
			
		||||
                e -> {
 | 
			
		||||
                    ctx.tellFailure(msg, e);
 | 
			
		||||
                });
 | 
			
		||||
                r -> {},
 | 
			
		||||
                e -> ctx.tellFailure(msg, e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -32,14 +32,11 @@ public class TbNotificationNodeConfiguration implements NodeConfiguration<TbNoti
 | 
			
		||||
    @NotEmpty
 | 
			
		||||
    private List<UUID> targets;
 | 
			
		||||
    @NotNull
 | 
			
		||||
    private NotificationTemplateId templateId;
 | 
			
		||||
    private NotificationRequestConfig additionalConfig;
 | 
			
		||||
    private UUID templateId;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TbNotificationNodeConfiguration defaultConfiguration() {
 | 
			
		||||
        TbNotificationNodeConfiguration config = new TbNotificationNodeConfiguration();
 | 
			
		||||
        config.setAdditionalConfig(new NotificationRequestConfig());
 | 
			
		||||
        return config;
 | 
			
		||||
        return new TbNotificationNodeConfiguration();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -441,7 +441,8 @@ export enum NotificationType {
 | 
			
		||||
  ALARM_ASSIGNMENT = 'ALARM_ASSIGNMENT',
 | 
			
		||||
  RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT = 'RULE_ENGINE_COMPONENT_LIFECYCLE_EVENT',
 | 
			
		||||
  ENTITIES_LIMIT = 'ENTITIES_LIMIT',
 | 
			
		||||
  API_USAGE_LIMIT = 'API_USAGE_LIMIT'
 | 
			
		||||
  API_USAGE_LIMIT = 'API_USAGE_LIMIT',
 | 
			
		||||
  RULE_ENGINE = 'RULE_ENGINE'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const NotificationTypeIcons = new Map<NotificationType, string | null>([
 | 
			
		||||
@ -527,12 +528,20 @@ export const NotificationTemplateTypeTranslateMap = new Map<NotificationType, No
 | 
			
		||||
    {
 | 
			
		||||
      name: 'notification.template-type.entities-limit',
 | 
			
		||||
      helpId: 'notification/entities_limit'
 | 
			
		||||
    }],
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  [NotificationType.API_USAGE_LIMIT,
 | 
			
		||||
    {
 | 
			
		||||
      name: 'notification.template-type.api-usage-limit',
 | 
			
		||||
      helpId: 'notification/api_usage_limit'
 | 
			
		||||
    }]
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  [NotificationType.RULE_ENGINE,
 | 
			
		||||
    {
 | 
			
		||||
      name: 'notification.template-type.rule-engine',
 | 
			
		||||
      helpId: 'notification/rule-engine'
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
export enum TriggerType {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										54
									
								
								ui-ngx/src/assets/help/en_US/notification/rule-engine.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								ui-ngx/src/assets/help/en_US/notification/rule-engine.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
			
		||||
#### Rule engine 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:
 | 
			
		||||
 | 
			
		||||
  * values from the incoming message metadata;
 | 
			
		||||
  * values from the incoming message data;
 | 
			
		||||
  * *originatorType* - type of the originator, e.g. 'Device';
 | 
			
		||||
  * *originatorId* - id of the originator
 | 
			
		||||
  * *msgType* - type of the message
 | 
			
		||||
  * *recipientEmail* - email of the recipient;
 | 
			
		||||
  * *recipientFirstName* - first name of the recipient;
 | 
			
		||||
  * *recipientLastName* - last name of the recipient;
 | 
			
		||||
 | 
			
		||||
Parameter names must be wrapped using `${...}`. For example: `${recipientFirstName}`. 
 | 
			
		||||
You may also modify the value of the parameter with one of the suffixes:
 | 
			
		||||
 | 
			
		||||
  * `upperCase`, for example - `${recipientFirstName:upperCase}`
 | 
			
		||||
  * `lowerCase`, for example - `${recipientFirstName:lowerCase}`
 | 
			
		||||
  * `capitalize`, for example - `${recipientFirstName:capitalize}`
 | 
			
		||||
 | 
			
		||||
<div class="divider"></div>
 | 
			
		||||
 | 
			
		||||
##### Examples
 | 
			
		||||
 | 
			
		||||
Let's assume the incoming message to Rule node has the following data:
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  "building_1": {
 | 
			
		||||
    "temperature": 24
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The following template:
 | 
			
		||||
 | 
			
		||||
```text
 | 
			
		||||
Building 1: temperature is ${building_1.temperature} 
 | 
			
		||||
{:copy-code}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
will be transformed to:
 | 
			
		||||
 | 
			
		||||
```text
 | 
			
		||||
Building 1: temperature is 24
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<br>
 | 
			
		||||
<br>
 | 
			
		||||
@ -2931,7 +2931,8 @@
 | 
			
		||||
            "entities-limit": "Entities limit",
 | 
			
		||||
            "entity-action": "Entity action",
 | 
			
		||||
            "general": "General",
 | 
			
		||||
            "rule-engine-lifecycle-event": "Rule engine lifecycle event"
 | 
			
		||||
            "rule-engine-lifecycle-event": "Rule engine lifecycle event",
 | 
			
		||||
            "rule-engine": "Rule engine"
 | 
			
		||||
        },
 | 
			
		||||
        "templates": "Templates",
 | 
			
		||||
        "tenant-profiles-list-rule-hint": "If the field is empty, the trigger will be applied to all tenant profiles",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user