/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.functional.junit4; import static org.mockito.Mockito.spy; import static org.mule.tck.junit4.AbstractMuleTestCase.TEST_CONNECTOR_LOCATION; import org.mule.runtime.api.message.Attributes; import org.mule.runtime.api.message.Message; import org.mule.runtime.api.metadata.DataType; import org.mule.runtime.api.metadata.MediaType; import org.mule.runtime.api.metadata.TypedValue; import org.mule.runtime.core.DefaultEventContext; import org.mule.runtime.core.api.Event; import org.mule.runtime.core.api.EventContext; import org.mule.runtime.core.api.connector.ReplyToHandler; import org.mule.runtime.core.api.construct.FlowConstruct; import org.mule.runtime.core.message.DefaultMultiPartPayload; import org.mule.runtime.core.message.GroupCorrelation; import java.io.Serializable; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import javax.activation.DataHandler; import org.apache.commons.collections.Transformer; import org.mockito.Mockito; import org.reactivestreams.Publisher; /** * Provides a fluent API for building events for testing. */ public class TestEventBuilder { private Object payload; private MediaType mediaType = MediaType.ANY; private Attributes attributes; private Map<String, Serializable> inboundProperties = new HashMap<>(); private Map<String, Serializable> outboundProperties = new HashMap<>(); private Map<String, DataHandler> inboundAttachments = new HashMap<>(); private Map<String, Attachment> outboundAttachments = new HashMap<>(); private Map<String, Object> sessionProperties = new HashMap<>(); private String sourceCorrelationId = null; private GroupCorrelation correlation = new GroupCorrelation(null, null); private Map<String, TypedValue> variables = new HashMap<>(); private ReplyToHandler replyToHandler; private Transformer spyTransformer = input -> input; private Publisher<Void> externalCompletionCallback = null; /** * Prepares the given data to be sent as the payload of the product. * * @param payload the payload to use in the message * @return this {@link TestEventBuilder} */ public TestEventBuilder withPayload(Object payload) { this.payload = payload; return this; } /** * Prepares the given data to be sent as the mediaType of the payload of the {@link Event} to the configured flow. * * @param mediaType the mediaType to use in the message * @return this {@link FlowRunner} */ public TestEventBuilder withMediaType(MediaType mediaType) { this.mediaType = mediaType; return this; } /** * Sets the {@link org.mule.runtime.api.message.Message#getAttributes()} value of the produced message * * @param attributes the attributes object for the produced {@link org.mule.runtime.api.message.Message} * @return this {@link TestEventBuilder} */ public TestEventBuilder withAttributes(Attributes attributes) { this.attributes = attributes; return this; } /** * Prepares a property with the given key and value to be sent as an inbound property of the product. * * @param key the key of the inbound property to add * @param value the value of the inbound property to add * @return this {@link TestEventBuilder} * @deprecated Transport infrastructure is deprecated. Use {@link Attributes} instead. */ @Deprecated public TestEventBuilder withInboundProperty(String key, Serializable value) { inboundProperties.put(key, value); return this; } /** * Prepares the given properties map to be sent as inbound properties of the product. * * @param properties the inbound properties to add * @return this {@link TestEventBuilder} * @deprecated Transport infrastructure is deprecated. Use {@link Attributes} instead. */ @Deprecated public TestEventBuilder withInboundProperties(Map<String, Serializable> properties) { inboundProperties.putAll(properties); return this; } /** * Prepares a property with the given key and value to be sent as an outbound property of the product. * * @param key the key of the outbound property to add * @param value the value of the outbound property to add * @return this {@link TestEventBuilder} * @deprecated Transport infrastructure is deprecated. Use {@link Attributes} instead. */ @Deprecated public TestEventBuilder withOutboundProperty(String key, Serializable value) { outboundProperties.put(key, value); return this; } /** * Prepares an attachment with the given key and value to be sent in the product. * * @param key the key of the attachment to add * @param value the {@link DataHandler} for the attachment to add * @return this {@link TestEventBuilder} * @deprecated Transport infrastructure is deprecated. Use {@link DefaultMultiPartPayload} instead. */ @Deprecated public TestEventBuilder withInboundAttachment(String key, DataHandler value) { inboundAttachments.put(key, value); return this; } /** * Prepares a property with the given key and value to be sent as a session property of the product. * * @param key the key of the session property to add * @param value the value of the session property to add * @return this {@link TestEventBuilder} * @deprecated Transport infrastructure is deprecated. */ @Deprecated public TestEventBuilder withSessionProperty(String key, Object value) { sessionProperties.put(key, value); return this; } /** * Configures the product event to have the provided {@code sourceCorrelationId}. See {@link Event#getCorrelationId()}. * * @return this {@link TestEventBuilder} */ public TestEventBuilder withSourceCorrelationId(String sourceCorrelationId) { this.sourceCorrelationId = sourceCorrelationId; return this; } /** * Configures the product event to have the provided {@code correlation}. See {@link Event#getGroupCorrelation()}. * * @return this {@link TestEventBuilder} */ public TestEventBuilder withCorrelation(GroupCorrelation correlation) { this.correlation = correlation; return this; } /** * Prepares a flow variable with the given key and value to be set in the product. * * @param key the key of the flow variable to put * @param value the value of the flow variable to put * @return this {@link TestEventBuilder} */ public TestEventBuilder withVariable(String key, Object value) { variables.put(key, new TypedValue(value, null)); return this; } /** * Prepares a flow variable with the given key and value to be set in the product. * * @param key the key of the flow variable to put * @param value the value of the flow variable to put * @param dataType the data type of the variable * @return this {@link TestEventBuilder} */ public TestEventBuilder withVariable(String key, Object value, DataType dataType) { variables.put(key, new TypedValue(value, dataType)); return this; } /** * Configures the product event to have the provided {@link ReplyToHandler}. * * @return this {@link TestEventBuilder} * @deprecated TODO MULE-10739 Move ReplyToHandler to compatibility module. */ @Deprecated public TestEventBuilder withReplyToHandler(ReplyToHandler replyToHandler) { this.replyToHandler = replyToHandler; return this; } /** * Will spy the built {@link Message} and {@link Event}. See {@link Mockito#spy(Object) spy}. * * @return this {@link TestEventBuilder} */ public TestEventBuilder spyObjects() { spyTransformer = input -> spy(input); return this; } public TestEventBuilder setExternalCompletionCallback(Publisher<Void> externalCompletionCallback) { this.externalCompletionCallback = externalCompletionCallback; return this; } /** * Produces an event with the specified configuration. * * @param flow the recipient for the event to be built. * @return an event with the specified configuration. */ public Event build(FlowConstruct flow) { final Message.Builder messageBuilder; messageBuilder = Message.builder().payload(payload).mediaType(mediaType); setInboundProperties(messageBuilder, inboundProperties); setOutboundProperties(messageBuilder, outboundProperties); if (attributes != null) { messageBuilder.attributes(attributes); } final Message muleMessage = messageBuilder.build(); EventContext eventContext; if (externalCompletionCallback != null) { eventContext = DefaultEventContext.create(flow, TEST_CONNECTOR_LOCATION, sourceCorrelationId, externalCompletionCallback); } else { eventContext = DefaultEventContext.create(flow, TEST_CONNECTOR_LOCATION, sourceCorrelationId); } Event.Builder builder = Event.builder(eventContext) .message((Message) spyTransformer.transform(muleMessage)).groupCorrelation(correlation) .flow(flow).replyToHandler(replyToHandler); for (Entry<String, TypedValue> variableEntry : variables.entrySet()) { builder.addVariable(variableEntry.getKey(), variableEntry.getValue().getValue(), variableEntry.getValue().getDataType()); } Event event = builder.build(); for (Entry<String, Attachment> outboundAttachmentEntry : outboundAttachments.entrySet()) { event = outboundAttachmentEntry.getValue().addOutboundTo(event, outboundAttachmentEntry.getKey()); } for (Entry<String, Object> sessionPropertyEntry : sessionProperties.entrySet()) { event.getSession().setProperty(sessionPropertyEntry.getKey(), sessionPropertyEntry.getValue()); } return (Event) spyTransformer.transform(event); } private void setInboundProperties(Message.Builder messageBuilder, Map<String, Serializable> inboundProperties) { // TODO(pablo.kraan): MULE-12280 - remove methods that use the legacy message API once all the tests using it are migrated try { Method inboundPropertiesMethod = messageBuilder.getClass().getMethod("inboundProperties", Map.class); inboundPropertiesMethod.invoke(messageBuilder, inboundProperties); } catch (Exception e) { throw new IllegalStateException(e); } } private void setOutboundProperties(Message.Builder messageBuilder, Map<String, Serializable> outboundProperties) { try { Method outboundPropertiesMethod = messageBuilder.getClass().getMethod("outboundProperties", Map.class); outboundPropertiesMethod.invoke(messageBuilder, outboundProperties); } catch (Exception e) { throw new IllegalStateException(e); } } private interface Attachment { Event addOutboundTo(Event event, String key); } }