Search in sources :

Example 1 with EmailNotificationMessage

use of org.openremote.model.notification.EmailNotificationMessage in project openremote by openremote.

the class EmailNotificationHandler method sendMessage.

@Override
public NotificationSendResult sendMessage(long id, Notification.Source source, String sourceId, Notification.Target target, AbstractNotificationMessage message) {
    // Check handler is valid
    if (!isValid()) {
        LOG.warning("SMTP invalid configuration so ignoring");
        return NotificationSendResult.failure("SMTP invalid configuration so ignoring");
    }
    List<EmailNotificationMessage.Recipient> toRecipients = new ArrayList<>();
    List<EmailNotificationMessage.Recipient> ccRecipients = new ArrayList<>();
    List<EmailNotificationMessage.Recipient> bccRecipients = new ArrayList<>();
    Notification.TargetType targetType = target.getType();
    String targetId = target.getId();
    switch(targetType) {
        case USER:
        case ASSET:
            // Recipient should be stored from earlier mapping call
            EmailNotificationMessage.Recipient recipient = (EmailNotificationMessage.Recipient) target.getData();
            if (recipient == null) {
                LOG.warning("User or asset recipient missing: id=" + targetId);
            } else {
                LOG.finest("Adding to recipient: " + recipient);
                toRecipients.add(recipient);
            }
            break;
        case CUSTOM:
            // This recipient list is the target ID
            Arrays.stream(targetId.split(";")).forEach(customRecipient -> {
                if (customRecipient.startsWith("to:")) {
                    String email = customRecipient.substring(3);
                    LOG.finest("Adding to recipient: " + email);
                    toRecipients.add(new EmailNotificationMessage.Recipient(email));
                } else if (customRecipient.startsWith("cc:")) {
                    String email = customRecipient.substring(3);
                    LOG.finest("Adding cc recipient: " + email);
                    ccRecipients.add(new EmailNotificationMessage.Recipient(email));
                } else if (customRecipient.startsWith("bcc:")) {
                    String email = customRecipient.substring(4);
                    LOG.finest("Adding bcc recipient: " + email);
                    bccRecipients.add(new EmailNotificationMessage.Recipient(email));
                } else {
                    LOG.finest("Adding to recipient: " + customRecipient);
                    toRecipients.add(new EmailNotificationMessage.Recipient(customRecipient));
                }
            });
            break;
        default:
            LOG.warning("Target type not supported: " + targetType);
    }
    EmailNotificationMessage emailNotificationMessage = (EmailNotificationMessage) message;
    EmailPopulatingBuilder emailBuilder = buildEmailBuilder(id, emailNotificationMessage);
    if (!toRecipients.isEmpty()) {
        toRecipients.forEach(recipient -> {
            if (!TextUtil.isNullOrEmpty(recipient.getAddress())) {
                emailBuilder.to(convertRecipient(recipient));
            }
        });
    }
    if (!ccRecipients.isEmpty()) {
        ccRecipients.forEach(recipient -> {
            if (!TextUtil.isNullOrEmpty(recipient.getAddress())) {
                emailBuilder.cc(convertRecipient(recipient));
            }
        });
    }
    if (!bccRecipients.isEmpty()) {
        bccRecipients.forEach(recipient -> {
            if (!TextUtil.isNullOrEmpty(recipient.getAddress())) {
                emailBuilder.bcc(convertRecipient(recipient));
            }
        });
    }
    if (emailBuilder.getRecipients().isEmpty()) {
        return NotificationSendResult.failure("No recipients set for " + targetType.name().toLowerCase() + ": " + targetId);
    }
    // Set from based on source if not already set
    if (emailBuilder.getFromRecipient() == null) {
        emailBuilder.from(defaultFrom);
    }
    return sendMessage(emailBuilder.buildEmail());
}
Also used : EmailNotificationMessage(org.openremote.model.notification.EmailNotificationMessage) EmailPopulatingBuilder(org.simplejavamail.email.EmailPopulatingBuilder) ArrayList(java.util.ArrayList) Recipient(org.simplejavamail.email.Recipient) Notification(org.openremote.model.notification.Notification)

Example 2 with EmailNotificationMessage

use of org.openremote.model.notification.EmailNotificationMessage in project openremote by openremote.

the class EmailNotificationHandler method getTargets.

@Override
public List<Notification.Target> getTargets(Notification.Source source, String sourceId, List<Notification.Target> targets, AbstractNotificationMessage message) {
    List<Notification.Target> mappedTargets = new ArrayList<>();
    if (targets != null) {
        targets.forEach(target -> {
            Notification.TargetType targetType = target.getType();
            String targetId = target.getId();
            switch(targetType) {
                case TENANT:
                case USER:
                    // Find all users in this tenant or by id
                    User[] users = targetType == Notification.TargetType.TENANT ? managerIdentityService.getIdentityProvider().queryUsers(new UserQuery().tenant(new TenantPredicate(targetId))) : managerIdentityService.getIdentityProvider().queryUsers(new UserQuery().ids(targetId));
                    if (users.length == 0) {
                        if (targetType == Notification.TargetType.USER) {
                            LOG.info("User not found: " + targetId);
                        } else {
                            LOG.info("No users found in target realm: " + targetId);
                        }
                        return;
                    }
                    mappedTargets.addAll(Arrays.stream(users).filter(user -> !Boolean.parseBoolean(user.getAttributes().getOrDefault(KEYCLOAK_USER_ATTRIBUTE_EMAIL_NOTIFICATIONS_DISABLED, Collections.singletonList("false")).get(0))).map(user -> {
                        Notification.Target userAssetTarget = new Notification.Target(Notification.TargetType.USER, user.getId());
                        userAssetTarget.setData(new EmailNotificationMessage.Recipient(user.getFullName(), user.getEmail()));
                        return userAssetTarget;
                    }).collect(Collectors.toList()));
                    break;
                case CUSTOM:
                    // Nothing to do here
                    mappedTargets.add(new Notification.Target(targetType, targetId));
                    break;
                case ASSET:
                    // Find descendant assets with email attribute
                    List<Asset<?>> assets = assetStorageService.findAll(new AssetQuery().select(new AssetQuery.Select().attributes(Asset.EMAIL.getName())).paths(new PathPredicate(targetId)).attributes(new AttributePredicate(new StringPredicate(Asset.EMAIL.getName()), new ValueEmptyPredicate().negate(true))));
                    if (assets.isEmpty()) {
                        LOG.fine("No assets with email attribute descendants of target asset");
                        return;
                    }
                    mappedTargets.addAll(assets.stream().map(asset -> {
                        Notification.Target assetTarget = new Notification.Target(Notification.TargetType.ASSET, asset.getId());
                        assetTarget.setData(new EmailNotificationMessage.Recipient(asset.getName(), asset.getEmail().orElse(null)));
                        return assetTarget;
                    }).collect(Collectors.toList()));
                    break;
            }
        });
    }
    EmailNotificationMessage email = (EmailNotificationMessage) message;
    // Map to/cc/bcc into a custom target for traceability in sent notifications
    List<String> addresses = new ArrayList<>();
    if (email.getTo() != null) {
        addresses.addAll(email.getTo().stream().map(EmailNotificationMessage.Recipient::getAddress).map(address -> "to:" + address).collect(Collectors.toList()));
        email.setTo((List<EmailNotificationMessage.Recipient>) null);
    }
    if (email.getCc() != null) {
        addresses.addAll(email.getCc().stream().map(EmailNotificationMessage.Recipient::getAddress).map(address -> "cc:" + address).collect(Collectors.toList()));
        email.setCc((List<EmailNotificationMessage.Recipient>) null);
    }
    if (email.getBcc() != null) {
        addresses.addAll(email.getBcc().stream().map(EmailNotificationMessage.Recipient::getAddress).map(address -> "bcc:" + address).collect(Collectors.toList()));
        email.setBcc((List<EmailNotificationMessage.Recipient>) null);
    }
    if (!addresses.isEmpty()) {
        mappedTargets.add(new Notification.Target(Notification.TargetType.CUSTOM, String.join(";", addresses)));
    }
    return mappedTargets;
}
Also used : AssetStorageService(org.openremote.manager.asset.AssetStorageService) Arrays(java.util.Arrays) AbstractNotificationMessage(org.openremote.model.notification.AbstractNotificationMessage) KEYCLOAK_USER_ATTRIBUTE_EMAIL_NOTIFICATIONS_DISABLED(org.openremote.manager.security.ManagerKeycloakIdentityProvider.KEYCLOAK_USER_ATTRIBUTE_EMAIL_NOTIFICATIONS_DISABLED) EmailPopulatingBuilder(org.simplejavamail.email.EmailPopulatingBuilder) TransportStrategy(org.simplejavamail.mailer.config.TransportStrategy) MapAccess.getInteger(org.openremote.container.util.MapAccess.getInteger) ArrayList(java.util.ArrayList) Level(java.util.logging.Level) Notification(org.openremote.model.notification.Notification) UserQuery(org.openremote.model.query.UserQuery) Recipient(org.simplejavamail.email.Recipient) org.openremote.model.query.filter(org.openremote.model.query.filter) EmailNotificationMessage(org.openremote.model.notification.EmailNotificationMessage) MapAccess.getBoolean(org.openremote.container.util.MapAccess.getBoolean) TextUtil(org.openremote.model.util.TextUtil) NotificationSendResult(org.openremote.model.notification.NotificationSendResult) User(org.openremote.model.security.User) ManagerIdentityService(org.openremote.manager.security.ManagerIdentityService) Asset(org.openremote.model.asset.Asset) MailerBuilder(org.simplejavamail.mailer.MailerBuilder) AssetQuery(org.openremote.model.query.AssetQuery) Mailer(org.simplejavamail.mailer.Mailer) ContainerService(org.openremote.model.ContainerService) Logger(java.util.logging.Logger) Constants(org.openremote.model.Constants) Collectors(java.util.stream.Collectors) Container(org.openremote.model.Container) List(java.util.List) EmailBuilder(org.simplejavamail.email.EmailBuilder) Email(org.simplejavamail.email.Email) Collections(java.util.Collections) EmailNotificationMessage(org.openremote.model.notification.EmailNotificationMessage) User(org.openremote.model.security.User) AssetQuery(org.openremote.model.query.AssetQuery) ArrayList(java.util.ArrayList) Recipient(org.simplejavamail.email.Recipient) Notification(org.openremote.model.notification.Notification) UserQuery(org.openremote.model.query.UserQuery) Asset(org.openremote.model.asset.Asset)

Example 3 with EmailNotificationMessage

use of org.openremote.model.notification.EmailNotificationMessage in project openremote by openremote.

the class JsonRulesBuilder method buildRuleActionExecution.

protected static RuleActionExecution buildRuleActionExecution(JsonRule rule, RuleAction ruleAction, String actionsName, int index, boolean useUnmatched, RulesFacts facts, RuleState ruleState, Assets assetsFacade, Users usersFacade, Notifications notificationsFacade, PredictedDatapoints predictedDatapointsFacade) {
    if (ruleAction instanceof RuleActionNotification) {
        RuleActionNotification notificationAction = (RuleActionNotification) ruleAction;
        if (notificationAction.notification != null) {
            Notification notification = notificationAction.notification;
            if (notification.getMessage() != null) {
                String body = null;
                boolean isEmail = Objects.equals(notification.getMessage().getType(), EmailNotificationMessage.TYPE);
                boolean isPush = Objects.equals(notification.getMessage().getType(), PushNotificationMessage.TYPE);
                boolean isHtml = false;
                if (isEmail) {
                    EmailNotificationMessage email = (EmailNotificationMessage) notification.getMessage();
                    isHtml = !TextUtil.isNullOrEmpty(email.getHtml());
                    body = isHtml ? email.getHtml() : email.getText();
                } else if (isPush) {
                    PushNotificationMessage pushNotificationMessage = (PushNotificationMessage) notification.getMessage();
                    body = pushNotificationMessage.getBody();
                }
                if (!TextUtil.isNullOrEmpty(body)) {
                    if (body.contains(PLACEHOLDER_TRIGGER_ASSETS)) {
                        // Need to clone the notification
                        notification = ValueUtil.clone(notification);
                        String triggeredAssetInfo = buildTriggeredAssetInfo(useUnmatched, ruleState, isHtml);
                        body = body.replace(PLACEHOLDER_TRIGGER_ASSETS, triggeredAssetInfo);
                        if (isEmail) {
                            EmailNotificationMessage email = (EmailNotificationMessage) notification.getMessage();
                            if (isHtml) {
                                email.setHtml(body);
                            } else {
                                email.setText(body);
                            }
                        } else if (isPush) {
                            PushNotificationMessage pushNotificationMessage = (PushNotificationMessage) notification.getMessage();
                            pushNotificationMessage.setBody(body);
                        }
                    }
                }
            }
            // Transfer the rule action target into notification targets
            Notification.TargetType targetType = Notification.TargetType.ASSET;
            if (ruleAction.target != null) {
                if (ruleAction.target.users != null && ruleAction.target.conditionAssets == null && ruleAction.target.assets == null && ruleAction.target.matchedAssets == null) {
                    targetType = Notification.TargetType.USER;
                } else if (ruleAction.target.custom != null && ruleAction.target.conditionAssets == null && ruleAction.target.assets == null && ruleAction.target.matchedAssets == null) {
                    targetType = Notification.TargetType.CUSTOM;
                }
            }
            Collection<String> ids = getRuleActionTargetIds(ruleAction.target, useUnmatched, ruleState, assetsFacade, usersFacade, facts);
            if (ids == null) {
                notification.setTargets((List<Notification.Target>) null);
            } else {
                Notification.TargetType finalTargetType = targetType;
                notification.setTargets(ids.stream().map(id -> new Notification.Target(finalTargetType, id)).collect(Collectors.toList()));
            }
            log(Level.FINE, "Sending notification for rule action: " + rule.name + " '" + actionsName + "' action index " + index);
            Notification finalNotification = notification;
            return new RuleActionExecution(() -> notificationsFacade.send(finalNotification), 0);
        }
    }
    if (ruleAction instanceof RuleActionWriteAttribute) {
        if (targetIsNotAssets(ruleAction.target)) {
            return null;
        }
        RuleActionWriteAttribute attributeAction = (RuleActionWriteAttribute) ruleAction;
        if (TextUtil.isNullOrEmpty(attributeAction.attributeName)) {
            log(Level.WARNING, "Attribute name is missing for rule action: " + rule.name + " '" + actionsName + "' action index " + index);
            return null;
        }
        Collection<String> ids = getRuleActionTargetIds(ruleAction.target, useUnmatched, ruleState, assetsFacade, usersFacade, facts);
        if (ids == null || ids.isEmpty()) {
            log(Level.INFO, "No targets for write attribute rule action so skipping: " + rule.name + " '" + actionsName + "' action index " + index);
            return null;
        }
        log(Level.FINE, "Writing attribute '" + attributeAction.attributeName + "' for " + ids.size() + " asset(s) for rule action: " + rule.name + " '" + actionsName + "' action index " + index);
        return new RuleActionExecution(() -> ids.forEach(id -> assetsFacade.dispatch(id, attributeAction.attributeName, attributeAction.value)), 0);
    }
    if (ruleAction instanceof RuleActionWait) {
        long millis = ((RuleActionWait) ruleAction).millis;
        if (millis > 0) {
            return new RuleActionExecution(null, millis);
        }
        log(Level.FINEST, "Invalid delay for wait rule action so skipping: " + rule.name + " '" + actionsName + "' action index " + index);
    }
    if (ruleAction instanceof RuleActionUpdateAttribute) {
        if (targetIsNotAssets(ruleAction.target)) {
            log(Level.FINEST, "Invalid target update attribute rule action so skipping: " + rule.name + " '" + actionsName + "' action index " + index);
            return null;
        }
        RuleActionUpdateAttribute attributeUpdateAction = (RuleActionUpdateAttribute) ruleAction;
        if (TextUtil.isNullOrEmpty(attributeUpdateAction.attributeName)) {
            log(Level.WARNING, "Attribute name is missing for rule action: " + rule.name + " '" + actionsName + "' action index " + index);
            return null;
        }
        List<String> matchingAssetIds;
        if (ruleAction.target == null || ruleAction.target.assets == null) {
            if (ruleAction.target != null && ruleAction.target.users != null) {
                throw new IllegalStateException("Cannot use action type '" + RuleActionUpdateAttribute.class.getSimpleName() + "' with user target");
            }
            matchingAssetIds = new ArrayList<>(getRuleActionTargetIds(ruleAction.target, useUnmatched, ruleState, assetsFacade, usersFacade, facts));
        } else {
            matchingAssetIds = facts.matchAssetState(ruleAction.target.assets).map(AssetState::getId).distinct().collect(Collectors.toList());
        }
        if (matchingAssetIds.isEmpty()) {
            log(Level.INFO, "No targets for update attribute rule action so skipping: " + rule.name + " '" + actionsName + "' action index " + index);
            return null;
        }
        // Look for the current value within the asset state facts (asset/attribute has to be in scope of this rule engine and have a rule state meta item)
        List<AssetState<?>> matchingAssetStates = matchingAssetIds.stream().map(assetId -> facts.getAssetStates().stream().filter(state -> state.getId().equals(assetId) && state.getName().equals(attributeUpdateAction.attributeName)).findFirst().orElseGet(() -> {
            log(Level.WARNING, "Failed to find attribute in rule states for attribute update: " + new AttributeRef(assetId, attributeUpdateAction.attributeName));
            return null;
        })).filter(Objects::nonNull).collect(Collectors.toList());
        if (matchingAssetStates.isEmpty()) {
            log(Level.WARNING, "No asset states matched to apply update attribute action to");
            return null;
        }
        return new RuleActionExecution(() -> matchingAssetStates.forEach(assetState -> {
            Object value = assetState.getValue().orElse(null);
            Class<?> valueType = assetState.getType().getType();
            boolean isArray = ValueUtil.isArray(valueType);
            if (!isArray && !ValueUtil.isObject(valueType)) {
                log(Level.WARNING, "Rule action target asset cannot determine value type or incompatible value type for attribute: " + assetState);
            } else {
                // Convert value to JSON Node to easily manipulate it
                value = isArray ? ValueUtil.convert(value, ArrayNode.class) : ValueUtil.convert(value, ObjectNode.class);
                switch(attributeUpdateAction.updateAction) {
                    case ADD:
                        if (isArray) {
                            value = value == null ? ValueUtil.JSON.createArrayNode() : value;
                            ((ArrayNode) value).add(ValueUtil.convert(attributeUpdateAction.value, JsonNode.class));
                        } else {
                            value = value == null ? ValueUtil.JSON.createObjectNode() : value;
                            ((ObjectNode) value).set(attributeUpdateAction.key, ValueUtil.convert(attributeUpdateAction.value, JsonNode.class));
                        }
                        break;
                    case ADD_OR_REPLACE:
                    case REPLACE:
                        if (isArray) {
                            value = value == null ? ValueUtil.JSON.createArrayNode() : value;
                            ArrayNode arrayValue = (ArrayNode) value;
                            if (attributeUpdateAction.index != null && arrayValue.size() >= attributeUpdateAction.index) {
                                arrayValue.set(attributeUpdateAction.index, ValueUtil.convert(attributeUpdateAction.value, JsonNode.class));
                            } else {
                                arrayValue.add(ValueUtil.convert(attributeUpdateAction.value, JsonNode.class));
                            }
                        } else {
                            value = value == null ? ValueUtil.JSON.createObjectNode() : value;
                            if (!TextUtil.isNullOrEmpty(attributeUpdateAction.key)) {
                                ((ObjectNode) value).set(attributeUpdateAction.key, ValueUtil.convert(attributeUpdateAction.value, JsonNode.class));
                            } else {
                                log(Level.WARNING, "JSON Rule: Rule action missing required 'key': " + ValueUtil.asJSON(attributeUpdateAction));
                            }
                        }
                        break;
                    case DELETE:
                        if (value != null) {
                            if (isArray) {
                                ((ArrayNode) value).remove(attributeUpdateAction.index);
                            } else {
                                ((ObjectNode) value).remove(attributeUpdateAction.key);
                            }
                        }
                        break;
                    case CLEAR:
                        if (isArray) {
                            value = ValueUtil.JSON.createArrayNode();
                        } else {
                            value = ValueUtil.JSON.createObjectNode();
                        }
                        break;
                }
                log(Level.FINE, "Updating attribute for rule action: " + rule.name + " '" + actionsName + "' action index " + index + ": " + assetState);
                assetsFacade.dispatch(assetState.getId(), attributeUpdateAction.attributeName, value);
            }
        }), 0);
    }
    log(Level.FINE, "Unsupported rule action: " + rule.name + " '" + actionsName + "' action index " + index);
    return null;
}
Also used : AssetStorageService(org.openremote.manager.asset.AssetStorageService) java.util(java.util) AttributeRef(org.openremote.model.attribute.AttributeRef) CronExpression(org.quartz.CronExpression) LocationAttributePredicate.getLocationPredicates(org.openremote.model.query.filter.LocationAttributePredicate.getLocationPredicates) ValueUtil(org.openremote.model.util.ValueUtil) Supplier(java.util.function.Supplier) ValueUtil.distinctByKey(org.openremote.model.util.ValueUtil.distinctByKey) ObjectNode(com.fasterxml.jackson.databind.node.ObjectNode) Level(java.util.logging.Level) Notification(org.openremote.model.notification.Notification) UserQuery(org.openremote.model.query.UserQuery) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) BiConsumer(java.util.function.BiConsumer) EmailNotificationMessage(org.openremote.model.notification.EmailNotificationMessage) JsonNode(com.fasterxml.jackson.databind.JsonNode) TextUtil(org.openremote.model.util.TextUtil) PersistenceEvent(org.openremote.model.PersistenceEvent) LogicGroup(org.openremote.model.query.LogicGroup) org.openremote.model.rules(org.openremote.model.rules) Asset(org.openremote.model.asset.Asset) MetaItemType(org.openremote.model.value.MetaItemType) Predicate(java.util.function.Predicate) AssetQuery(org.openremote.model.query.AssetQuery) AttributePredicate(org.openremote.model.query.filter.AttributePredicate) Collectors(java.util.stream.Collectors) ArrayNode(com.fasterxml.jackson.databind.node.ArrayNode) AtomicLong(java.util.concurrent.atomic.AtomicLong) PushNotificationMessage(org.openremote.model.notification.PushNotificationMessage) Stream(java.util.stream.Stream) org.openremote.model.rules.json(org.openremote.model.rules.json) TimerService(org.openremote.container.timer.TimerService) TimeUtil(org.openremote.model.util.TimeUtil) AssetQueryPredicate.groupIsEmpty(org.openremote.manager.rules.AssetQueryPredicate.groupIsEmpty) AttributeRef(org.openremote.model.attribute.AttributeRef) Notification(org.openremote.model.notification.Notification) ArrayNode(com.fasterxml.jackson.databind.node.ArrayNode) EmailNotificationMessage(org.openremote.model.notification.EmailNotificationMessage) ObjectNode(com.fasterxml.jackson.databind.node.ObjectNode) PushNotificationMessage(org.openremote.model.notification.PushNotificationMessage)

Aggregations

EmailNotificationMessage (org.openremote.model.notification.EmailNotificationMessage)3 Notification (org.openremote.model.notification.Notification)3 ArrayList (java.util.ArrayList)2 Level (java.util.logging.Level)2 Collectors (java.util.stream.Collectors)2 AssetStorageService (org.openremote.manager.asset.AssetStorageService)2 Asset (org.openremote.model.asset.Asset)2 AssetQuery (org.openremote.model.query.AssetQuery)2 UserQuery (org.openremote.model.query.UserQuery)2 TextUtil (org.openremote.model.util.TextUtil)2 EmailPopulatingBuilder (org.simplejavamail.email.EmailPopulatingBuilder)2 Recipient (org.simplejavamail.email.Recipient)2 JsonNode (com.fasterxml.jackson.databind.JsonNode)1 ArrayNode (com.fasterxml.jackson.databind.node.ArrayNode)1 ObjectNode (com.fasterxml.jackson.databind.node.ObjectNode)1 java.util (java.util)1 Arrays (java.util.Arrays)1 Collections (java.util.Collections)1 List (java.util.List)1 ScheduledExecutorService (java.util.concurrent.ScheduledExecutorService)1