Message Content Handling Analysis and Design

The idea behind Message Content Handling is to provide a mechanism in UCS to specify different content for a Message when it is delivered through different Channels or to different Recipients.

Use Case 1
Different bodies of the same Message must be sent to different Channels: i.e. body[0] -> SMS, body[1] -> EMIAL.

Use Case 2
Different bodies of the same Message must be sent to different Recipients: i.e. body[0] -> recipient[0], body[1] -> recipient[1]

Note: Message Content Handling can already be achieved by a combination of multiple Messages and/or re-routing and escalation. The functionality specified in this document is merely a simplification to achieve the desired behavior using a single Message instead of multiple ones.

Analysis

Option 1

Message class allows multiple bodies to be configured via its 'MessageBody[] parts' attribute.
A MessageBody has the following attributes:

  • String messageBodyId: A unique Id of this body. According to the specification, this id allow other parts of the Message to make reference to this body. The current implementation of ucs-api doesn't reflect this though.
  • String messageId: The id of the Message this Body belongs to.
  • String content: The content of the Body as String.
  • String tag: Multi-purpose field that, according to the specification, "providers [a way] to organize information in the body independent of the type of content".
  • String type: The MIME type of the content of this Body.

Probably the easiest way to implement this Message Content Handling functionality is by using MessageBody's tag property. Unfortunately, this property is just a String, so if we want to use it to implement Message Content Handling, we will need to specify some kind of format to be used.

The proposed solution is to use the following format in MessageBody's tag property:

  • "[SERVICE-ID]:<Service id>". i.e: "[SERVICE-ID]:EMIAL". The service id must match the service Id used in the recipients' addresses.
  • "[RECIPIENT-ID]:<Recipient id>". i.e. "[RECIPIENT-ID]:1234". The recipient id must match one of the Message's recipients.

The implementation is as follows:
When a message is going to be sent to a Recipient, the Message's bodies are analyzed as follows:
1.- If there is a MessageBody whose tag attribute matches "[RECIPIENT-ID]:<Recipient id>" (where <recipient id> is the id of the recipient being processed), then the body is selected to be delivered. If multiple MessageBodies match the same RECIPIENT-ID, the first one has precedence (Only the first match should be delivered).
2.- If the MessageBody didn't contain any tag matching the specific recipient (previous step), the MessageBodies are analyzed to check if there is at least 1 MessageBody whose tag matches "[SERVICE-ID]:<service id>" (where <service id> is the id of the service being processed). If there is a match, then that MessageBody is used as the content of the delivered message. If multiple MessageBodies match the same SERVICE-ID, the first one has precedence (Only the first match should be delivered).
3.- If the previous 2 steps didn't throw any result, then the first MessageBody (without any other meaningful tag) of the Message is used as the content of the message being delivered.

Bulk channels

Some of the Channels supported by UCS may be considered "bulk" channels. A bulk channel is a channel that supports multiple recipients of the same message at the same time. An example of this type of channels is a Group Chat. As a general rule, when a Message is delivered through a bulk channel, the "[RECIPIENT-ID]" tag is disregarded by UCS.

Option 2

Another option to implement Message Content Handling is to use the MessageHeader's 'properties' attribute instead of the tag attribute of each MessageBody.

The idea here is similar to Option 1: specify what MessageBody should be delivered based on RecipientId or ServiceId.

The implementation is as follows:
When a message is going to be sent to a Recipient, the Message's header is analyzed as follows:
1.- If there is a property whose key matches "[RECIPIENT-ID]:<Recipient id>" (where <recipient id> is the id of the recipient being processed), then the value of that property is used as the index of the MessageBody that has to be delivered. If multiple keys match the same RECIPIENT-ID, the first one has precedence (Only the first match should be delivered).
2.- If the MessageHeader didn't contain any key matching the specific recipient (previous step), the MessageHeader's properties are analyzed to check if there is at least 1 key who matches "[SERVICE-ID]:<service id>" (where <service id> is the id of the service being processed). If there is a match, then the value of that property is used as the index of the MessageBody that has to be delivered. If multiple keys match the same SERVICE-ID, the first one has precedence (Only the first match should be delivered).

Bulk channels

Some of the Channels supported by UCS may be considered "bulk" channels. A bulk channel is a channel that supports multiple recipients of the same message at the same time. An example of this type of channels is a Group Chat. As a general rule, when a Message is delivered through a bulk channel, the "[RECIPIENT-ID]" key is disregarded by UCS. 

Design

In the current implementation, Message's "parts" attribute is used in the following processors: 

  • UCSPreprateChat -> uses parts[0] as the content of the chat message.
  • UCSPrepareSMS -> uses parts[0] as the content of the SMS.
  • UCSPrepareTextToVoide -> uses parts[0] as the message.
  • UCSRouteMessageOnContext -> evaluates a regex against parts[0] and route the FlowFile accordingly.
  • UCSConvertChatResponseToMessage -> uses parts[0] to store the conent of the incoming chat message.
  • UCSConvertSMSResponseToMessage -> uses parts[0] to store the conent of the incoming SMS message.

 

For the first 3 cases (UCSPreprateChat, UCSPrepareSMS and UCSPrepareTextToVoide)we need to modify the processors to evaluate which part of a MessageBody needs to be used. Given that the required functionality is the same in the 3 procesors, we can extract this logic into a helper class: MessageBodyResolver.
The case of UCSRouteMessageOnContext is different. For this processor, the index of the part that should be used to evaluate the regex must be part of the processor configuration. A new property has to be added to this processor to allow the index to be configured. The default value of this new property should be '0'. The property should only allow positive integers (including 0) and it should also support expression language.
For the last 2 processors (UCSConvertChatResponseToMessage and UCSConvertSMSResponseToMessage), we assume that it is correct to use parts[0] to store the message. These 2 processors shouldn't be modified.

MessageBodyResolver class

A new class with the name of MessageBodyResolver must be implemented. This class will contain the required logic to determine which part of a Message should be used by a processor given a context. The context required by this resolution is the service Id, the recipient id and the recipient address being processed. Note that in some situations, some ( or all) of the context's pieces could be missing.
The logic required by this class is described in the analysis section of this document.
The proposed interface for MessageBodyResolver is:

public class MessageBodyResolver {
    public static MessageBody resolveMessagePart(Message message, String serviceId, String recipientId, String recipientAddress);
}

UCSPrepareChat invocation context

In UCSPrepareChat processor, the following resolution context information is available:

  • serviceId: value of SERVICE_ID property
  • recipientId: N/A
  • recipientAddress: N/A


UCSPrepareSMS invocation context

In UCSPrepareSMS processor, the following resolution context information is available:

  • serviceId: value of SMS_SERVICE_ID property
  • recipientId: the id of each individual recipient
  • recipientAddress: the PhisycalAddress.address of each individual recipient.