Skip to content

Commit

Permalink
MID-7487: added transport's recipientAddressExpression
Browse files Browse the repository at this point in the history
This resolves Focus to address string, if not resolved yet.
  • Loading branch information
virgo47 committed Feb 25, 2022
1 parent f21878b commit a7b6e12
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010-2019 Evolveum and contributors
* Copyright (C) 2010-2022 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
Expand Down Expand Up @@ -155,6 +155,7 @@ public class ExpressionConstants {
public static final String VAR_MESSAGE_TEXT = "messageText";
public static final String VAR_ENCODED_MESSAGE_TEXT = "encodedMessageText";
public static final String VAR_MESSAGE = "message";
public static final String VAR_RECIPIENT = "recipient";
public static final String VAR_TEXT_FORMATTER = "textFormatter";
public static final String VAR_NOTIFICATION_FUNCTIONS = "notificationFunctions";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2229,6 +2229,22 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="recipientAddressExpression" type="tns:ExpressionType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Optional expression for recipient address returned as a String.
This is useful when the recipient is provided as a Focus object (e.g. from the notifier),
this object is available as `recipient` input variable into the expression.
This is not used if the recipient address was already resolved and is available
only as a String instead of a Focus.
If this expression is not specified, the default conversion provided by the method
`getDefaultRecipientAddress(Focus)` in the `Transport` implementation will be used.
</xsd:documentation>
<xsd:appinfo>
<a:displayName>GeneralTransportConfigurationType.whiteList</a:displayName>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="debug" type="xsd:boolean" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/
package com.evolveum.midpoint.notifications.impl.notifiers;

import static com.evolveum.midpoint.schema.constants.ExpressionConstants.VAR_RECIPIENT;

import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -163,7 +165,7 @@ private int prepareAndSendMessage(E event, N notifierConfig, VariablesMap variab
// But this will also mean rewriting existing tests from legacy to new transport style.
// String transportName = transport.getName();

String address = getRecipientAddress(transport, recipient);
String address = getRecipientAddress(event, transport, recipient, task, result);
if (address == null) {
getLogger().debug("Skipping notification as no recipient address was provided for transport={}", transportName);
result.recordStatus(OperationResultStatus.NOT_APPLICABLE, "No recipient address provided be notifier or transport");
Expand Down Expand Up @@ -249,20 +251,33 @@ private int prepareAndSendMessage(E event, N notifierConfig, VariablesMap variab
}

@Nullable
private String getRecipientAddress(Transport<?> transport, RecipientExpressionResultType recipient) {
private String getRecipientAddress(E event, Transport<?> transport,
RecipientExpressionResultType recipient, Task task, OperationResult result) {
String address = recipient.getAddress();
if (address == null) {
ObjectReferenceType recipientRef = recipient.getRecipientRef();
if (recipientRef != null) {
Objectable object = recipientRef.asReferenceValue().getOriginObject();
if (object instanceof FocusType) {
address = transport.getDefaultRecipientAddress((FocusType) object);
return getRecipientAddressFromFocus(event, transport, (FocusType) object, task, result);
}
}
}
return address;
}

private String getRecipientAddressFromFocus(E event,
Transport<?> transport, FocusType focus, Task task, OperationResult result) {
ExpressionType recipientAddressExpression = transport.getConfiguration().getRecipientAddressExpression();
if (recipientAddressExpression != null) {
VariablesMap variables = new VariablesMap();
variables.put(VAR_RECIPIENT, focus, FocusType.class);
return getStringFromExpression(event, variables, task, result,
recipientAddressExpression, "recipient address expression", true);
}
return transport.getDefaultRecipientAddress(focus);
}

@Nullable
private String getBody(E event, N notifierConfig, MessageTemplateContentType messageContent, VariablesMap variables, String transportName,
Task task, OperationResult result) throws SchemaException {
Expand Down Expand Up @@ -449,25 +464,25 @@ private String getContentTypeFromExpression(E event, N generalNotifierType, Vari
private String getStringFromExpression(E event, VariablesMap variables,
Task task, OperationResult result, ExpressionType expression, String expressionTypeName, boolean canBeNull) {
if (expression != null) {
List<String> contentTypeList = evaluateExpressionChecked(expression, variables,
expressionTypeName + " expression", task, result);
if (contentTypeList == null || contentTypeList.isEmpty()) {
List<String> resultValues = evaluateExpressionChecked(
expression, variables, expressionTypeName + " expression", task, result);
if (resultValues == null || resultValues.isEmpty()) {
getLogger().debug(expressionTypeName + " expression for event " + event.getId() + " returned nothing.");
return canBeNull ? null : "";
}
if (contentTypeList.size() > 1) {
if (resultValues.size() > 1) {
getLogger().warn(expressionTypeName + " expression for event " + event.getId() + " returned more than 1 item.");
}
return contentTypeList.get(0);
return resultValues.get(0);
} else {
return null;
}
}

private String getBodyFromExpression(E event, @NotNull ExpressionType bodyExpression, VariablesMap variables,
Task task, OperationResult result) {
List<String> bodyList = evaluateExpressionChecked(bodyExpression, variables,
"body expression", task, result);
List<String> bodyList = evaluateExpressionChecked(
bodyExpression, variables, "body expression", task, result);
if (bodyList == null || bodyList.isEmpty()) {
getLogger().warn("Body expression for event {} returned nothing.", event.getId());
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,20 +502,6 @@ public void test160LocalizedMessageTemplateAttachmentInheritance() throws Except
.anyMatch(a -> getRawValue(a.getContent()).equals("default-content2"));
}

private Message getSingleMessage(TestMessageTransport testTransport) {
assertThat(testTransport.getMessages()).hasSize(1);
Message message = testTransport.getMessages().get(0);
return message;
}

private Object getRawValue(Object value) {
try {
return RawType.getValue(value);
} catch (SchemaException e) {
throw new RuntimeException(e);
}
}

@Test
public void test200RecipientExpressionReturningFocus() throws Exception {
OperationResult result = getTestOperationResult();
Expand All @@ -527,7 +513,7 @@ public void test200RecipientExpressionReturningFocus() throws Exception {
.replace(new NotificationConfigurationType(prismContext)
.handler(new EventHandlerType()
.generalNotifier(new GeneralNotifierType()
// provided with the event below
// requestee provided with the event below
.recipientExpression(groovyExpression("return requestee"))
.bodyExpression(velocityExpression(messageBody))
.transport("test"))))
Expand All @@ -542,7 +528,6 @@ public void test200RecipientExpressionReturningFocus() throws Exception {
// This is used as default recipient, no recipient results in no message.
event.setRequestee(new SimpleObjectRefImpl(notificationFunctions,
new UserType(prismContext)
.preferredLanguage("sk")
// this will be returned by TestMessageTransport.getDefaultRecipientAddress
.emailAddress("user@example.com")));
notificationManager.processEvent(event, getTestTask(), result);
Expand All @@ -565,7 +550,6 @@ public void test210RecipientExpressionReturningLiteralValue() throws Exception {
.replace(new NotificationConfigurationType(prismContext)
.handler(new EventHandlerType()
.generalNotifier(new GeneralNotifierType()
// provided with the event below
.recipientExpression(literalExpression("literal@example.com"))
.bodyExpression(velocityExpression(messageBody))
.transport("test"))))
Expand All @@ -586,6 +570,48 @@ public void test210RecipientExpressionReturningLiteralValue() throws Exception {
assertThat(message.getBody()).isEqualTo(messageBody);
}

@Test
public void test300MessageTransportUsingRecipientAddressExpression() throws Exception {
OperationResult result = getTestOperationResult();

given("configuration with transport using recipient address expression");
String messageBody = "This is message body"; // velocity template without any placeholders
Collection<? extends ItemDelta<?, ?>> modifications = prismContext.deltaFor(SystemConfigurationType.class)
.item(SystemConfigurationType.F_MESSAGE_TRANSPORT_CONFIGURATION)
.replace(new MessageTransportConfigurationType(prismContext)
.customTransport(new CustomTransportConfigurationType(prismContext)
.name("test")
.recipientAddressExpression(groovyExpression("this.binding.variables.each {k,v -> println \"$k = $v\"};\n"
+ "return 'xxx' + recipient.emailAddress"))
.type(TestMessageTransport.class.getName())))
.item(SystemConfigurationType.F_NOTIFICATION_CONFIGURATION)
.replace(new NotificationConfigurationType(prismContext)
.handler(new EventHandlerType()
.generalNotifier(new GeneralNotifierType()
// requestee provided with the event below
.recipientExpression(groovyExpression("return requestee"))
.bodyExpression(velocityExpression(messageBody))
.transport("test"))))
.asItemDeltas();
repositoryService.modifyObject(
SystemConfigurationType.class, SYS_CONFIG_OID, modifications, result);
TestMessageTransport testTransport = (TestMessageTransport) transportService.getTransport("test");
assertThat(testTransport.getMessages()).isEmpty();

when("event with requestee is sent to notification manager");
CustomEventImpl event = createCustomEvent();
event.setRequestee(new SimpleObjectRefImpl(notificationFunctions,
new UserType(prismContext).emailAddress("user@example.com")));
notificationManager.processEvent(event, getTestTask(), result);

then("transport sends the message");
Message message = getSingleMessage(testTransport);
assertThat(message).isNotNull();
and("address is based on notifier/recipientExpression -> transport/recipientAddressExpression chain");
assertThat(message.getTo()).containsExactlyInAnyOrder("xxxuser@example.com");
assertThat(message.getBody()).isEqualTo(messageBody);
}

@Test
public void test900NotifierWithoutTransportDoesNotSendAnything() throws Exception {
given("configuration with notifier without transport");
Expand Down Expand Up @@ -716,4 +742,18 @@ private CustomEventImpl createCustomEvent() {
null, // TODO why is this not nullable?
EventStatusType.SUCCESS, "test-channel");
}

private Message getSingleMessage(TestMessageTransport testTransport) {
assertThat(testTransport.getMessages()).hasSize(1);
Message message = testTransport.getMessages().get(0);
return message;
}

private Object getRawValue(Object value) {
try {
return RawType.getValue(value);
} catch (SchemaException e) {
throw new RuntimeException(e);
}
}
}

0 comments on commit a7b6e12

Please sign in to comment.