Skip to content

Channel and Service Implementation

Rowan Pillay edited this page Jun 24, 2019 · 2 revisions

The channel and service implementation is the means by which the mACM Message Mediator is able to communicate with external messaging systems such as Twilio and Clickatell. The component supports a pluggable architecture to simplify the process of implementing new channels and services. The component relies on the configuration file (config.json) and data coming through from the "CommunicationRequest.channel" extension of the CommunicationRequest resource to know which channel and service to use.

Channel

A channel is a representation of a message delivery mechanism, examples include SMS, WhatsApp, App notifications, and many more. A channel can have one or many services. The component also has a default "channel" to support services that might not easily fit into one category.

New channels can be added by creating a folder under the /src/channels folder with the name of the channel and then implementing the following IChannel interface in an index.ts within this folder.

export interface IChannel {
 /**
  * Transform a CommunicationRequest into an object that conforms to INotificationRequest interface
  *
  * @param {CommunicationRequest} communicationRequest
  * @return {INotificationRequest}
  */
 createNotificationRequest (communicationRequest: CommunicationRequest, props: Object, extensions: Object[])
   : INotificationRequest;
}

Service

A service, on the other hand, is the actual implementation that communicates with an external messaging service such as Nexmo. A service can be thought of as an adapter/proxy for communicating with external messaging services.

New services can be added by extending the abstract MessagingInterface interface below in a file named according to the service name, in the /src/ folder. Note that the processStatusRequest method should be overridden if the status of a message must be fetched from the external service.

export abstract class MessagingService {
 /**
  * Processes a INotificationRequest and sends an alert/notification.
  *
  * @param {INotificationRequest} notificationRequest
  * @return {Promise<CommunicationResource>}
  */
 abstract processNotification(notificationRequest: INotificationRequest) : Promise<INotificationResponse>;

 /**
  * Processes an API callback from the implemented messaging service.
  *
  * @param {any} data
  * @returns {Promise<CommunicationResource>}
  */
 abstract processWebhook(data: any) : Promise<IWebhookResponse>;

 /**
  * Processes a request for the delivery status of an alert/notification.
  * NOTE: This is an implementation of the IHE mACM profile transaction ITI-85
  *
  * @param {string} communicationRequestId
  * @returns {Promise<CommunicationResource>}
  */
 processStatusRequest(resource: ResourceType, params: Object)
   : Promise<any> {
   return new Promise((resolve, reject) => {
     const fhirStoreUrl = buildHearthUrl({
       host: config.get(EnvKeys.HearthHost) as string,
       port: config.get(EnvKeys.HearthPort) as PortNumber,
       secured: config.get(EnvKeys.HearthSecured) as boolean,
       path: `fhir/${resource}`
     });

     fhirStore
       .searchForResources(fhirStoreUrl, params)
       .then(resolve)
       .catch(reject);
   });
 }

CommunicationRequest.channel Extension

Example
{
  "url": "Communicaiton.channel",
  "valueString": "sms:twilio"
}
Field Value Description
url Communicaiton.channel The url field describes the extension to be used as defined in the FHIR specification. This field is required and must be set to "Communication.channel"
valueString [channel]:[service] This field describes the data type and value of the extension. In our case, the value must be a string containing the channel and service name separated by a colon.

Channel and Service configuration

{
 "channels": {
   "webhook": {
     "host": "<host>",
     "protocol": "http",
     "port": 80
   },
   "metadata": [
     {
       "type": "sms",
       "default": true,
       "services": [
         {
           "default": true,
           "name": "twilio",
           "props": {
             "token": "<token>",
             "sid": "<sid>",
             "from": "<from>",
             "webhookActive": true             
           }
         },
         {
           "name": "clickatell",
           "props": {
             "clickatellApiKey": "<clickatellApiKey>",
             "url": "https://platform.clickatell.com/messages/http/send"
           }
         }
       ]
     },
     {
       "type": "other",
       "default": true,
       "services": [
         {
           "name": "rapidpro",
           "props": {
             "flowApiUrl": "",
             "token": "",
             "flow": ""
           }
         }
       ]
     }
   ]
 }

Description

Field Constraint Description
webhook optional Used to specify the host to be used by service that need to send message delivery status updates via webhooks.
webhook.host required The host address to be used for the webhook, this address could be any valid domain name or IP address.
webhook.protocol required The HTTP protocol to used, this should be "Http" or “Https”
webhook.port optional The port number to be used. The service will default to port 80 when this value is not specified.
metadata required The metadata field is used to configure all channels and services. This field must contain at least one channel with at least one service.
metadata.type required The identifier for the channel, there values here could be “sms”, “whatsapp”, “other”, or any other new channel being defined.
metadata.default optional Specifies whether this channel is to be used as the default channel or not. Channels are used as default channels when the CommunicationRequest resource does not specify the channel in the “CommunicaitonRequest.channel” extension.
metadata.services required Defines a collection of services for the channel.
metadata.service.default optional Specifies when this service is to be used as the default service or not. Services are used as default services when CommunicationRequest resource does not specify the channel or service in the “CommunicationRequest.channel” extension.
metadata.service.name required This is the name of the service and must match the name of the module implementing the service. The name is case-sensitive as it used to match module names in the code.
metadata.service.props optional The component allows extra data to be passed to services via “props”. Props are extra fields required by the service and accessible via the message service interface.