On this page:

5.8 Subscription

 

IMPORTANT UPGRADE NOTE: When upgrading CDR from versions 2019.05 and earlier, if subscriptions are enabled, then you will need to add a new Subscription Matching Module of the same FHIR version as your FHIR Storage module and link the Subscription Matching module to that FHIR Storage module as a dependency.

The FHIR standard specifies a powerful mechanism for push-based notification of changes in the repository. This mechanism is called Subscription.

A Subscription is a publish-subscribe (pub/sub) mechanism built into the FHIR specification. Subscriptions are defined using the Subscription resource, which specifies what type of data is being subscribed to and how the recipient should be notified.

The Subscription Resource

A simple example of a Subscription resource follows:

{
  "resourceType": "Subscription",
  "status": "requested",
  "criteria": "Patient?name=smith",
  "channel": {
    "type": "rest-hook",
    "endpoint": "http://example.com:8080/fhir",
    "payload": "application/json"
  }
}

The Subscription resource contains several important fields:

  • status – When a Subscription is created by a client, it should have a status of requested. Smile CDR will process this Subscription, and if it is valid, the status will automatically be changed to active, meaning that the Subscription is now being processed.
  • criteria – This is a FHIR search query URL that will be used to determine which resources apply to the Subscription. These resources will trigger a notification when they change. See Criteria below for more information.
  • channel.type – This is the mechanism for delivery, such as rest-hook, websocket, email, sms, or message. See Channel Types below for more information.
  • channel.endpoint – For channel types that require the server to initiate a connection to the client, this is the URL of the endpoint to which the server should connect.
  • channel.payload – This is the mimetype encoding to use (e.g. application/fhir+json for JSON data).

5.8.1 Smile CDR Subscription Architecture

 

An overview of the Smile CDR Subscription architecture is illustrated in the following diagram:

Subscription Overview

Note that subscription processing is not enabled by default. If you wish to use subscription processing in your system, you need to make the following changes:

1) Enable the type(s) of subscriptions your server will activate on the FHIR Storage module by using specific configuration properties for the types of subscriptions you wish to enable. For example, the subscription.rest_hook.enabled configuration property may be used to enable REST Hook subscriptions on your FHIR Storage module (see below).
2) Add a Subscription Matcher module that depends on this FHIR Storage module.
3) If you are enabling websocket subscriptions, you will also need to add a Websocket Subscription module that depends on this Subscription Matcher module. If you would like your websocket endpoint included in your FHIR Endpoint’s capability statement, then you can add a dependency from that FHIR Endpoint Module onto this Websocket Subscription module.

5.8.2 Subscription Matcher Module

 

If subscriptions are enabled on a FHIR Storage Module, then that module will create a Matching Queue and send all created/updated resources to that queue. This queue is drained by a Subscription Matching module. This Subscription Matcher module needs to be configured to point to the Storage Module where you enabled subscriptions. The Subscription Matcher module uses its FHIR Storage module to retrieve two resource types required for subscription matching: Subscription and SearchParameter. The Subscription Matcher module maintains in-memory caches of both of these resources that it updates from its FHIR Storage module every 60 seconds. A new subscription will start matching immediately on the node where it was persisted. In a clustered configuration, it can take up to 60 seconds for a new subscription to start matching on all nodes.

The Subscription Matcher tries to perform most criteria matching in-memory. When in-memory matching isn’t possible, the Subscription Matcher module queries the Storage Module to determine if there’s a match.

5.8.3 Criteria

 

When any resources that match Subscription.criteria are created or updated, the specified notification will occur. For example, if the Subscription specifies a criteria of Patient?name=smith then a notification will occur any time a Patient named “smith” is created or updated.

5.8.4 Channel Types

 

The following sections outline the various channel types (delivery mechanisms) that are supported by Smile CDR.

5.8.5 Channel Type: rest-hook

 

The rest-hook channel type specifies that Smile CDR should create an HTTP REST request to a FHIR endpoint any time that a resource changes that matches the given subscription.

For example, suppose a Subscription is created with the following values:

{
  "resourceType": "Subscription",
  "status": "requested",
  "criteria": "Patient?name=smith",
  "channel": {
    "type": "rest-hook",
    "endpoint": "http://example.com:8080/fhir",
    "payload": "application/json"
  }
}

In this case, if a new Patient is created with the name “Smith”, and the FHIR Storage module assigns it an ID of 123, an HTTP PUT will be performed to the address http://example.com:8080/fhir/Patient/123 with the resource body as the payload.

5.8.6 Channel Type: email

 

The email channel type uses an SMTP relay to deliver notification about changed resources to the recipient. The contents, recipient, and body of the transmitted email may be specified using properties in the Subscription.

For example, the following example shows a simple email subscription:

{
  "resourceType": "Subscription",
  "status": "requested",
  "reason": "Monitor new emergency department encounters",
  "criteria": "Encounter?class=http://hl7.org/fhir/v3/ActCode|EMER",
  "channel": {
    "type": "email",
    "endpoint": "mailto:foo@example.com",
    "payload": "An Emergency Department encounter has been created or updated."
  }
}

Note the following fields:

  • endpoint – The endpoint must be set to a `mailto:` URI that will be treated as the recipient email address. Additional recipients may be specified by using comma-separated values (e.g. mailto:recipient1@example.com,recipient2@example.com).
  • payload – The payload is used as the body of the email.

Email Extensions

HAPI FHIR and Smile CDR provide several extensions that may be placed on the Subscription.channel element:

URLTypeDescription
http://hapifhir.io/fhir/StructureDefinition/subscription-email-from uri If specified, this extension contains the "from" address that the email will show as the sender.
http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template string If specified, this extension contains the subject of the email being sent.

An example resource including these extensions is shown below.

{
  "resourceType": "Subscription",
  "status": "requested",
  "reason": "Monitor new emergency department encounters",
  "criteria": "Encounter?class=http://hl7.org/fhir/v3/ActCode|EMER",
  "channel": {
    "extension": [
      {
        "url": "http://hapifhir.io/fhir/StructureDefinition/subscription-email-from",
        "valueUri": "mailto:myfrom@from.com"
      },
      {
        "url": "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template",
        "valueString": "This is a subject."
      }
    ],
    "type": "email",
    "endpoint": "mailto:foo@example.com",
    "payload": "This is the body."
  }
}

5.8.7 Channel Type: websocket

 

Websocket subscriptions are useful when the endpoint that needs to be notified has a user-interface like a web application or a mobile app. When a websocket subscription is activated on Smile CDR, the Smile CDR opens a websocket that listens for client websocket connections. When a client connects, the client indicates a subscription id it is interested in and then when incoming resources match that subscription, Smile CDR will notify the client over that websocket. This can be useful, for example, a in resource dashboad application that need to be refreshed as new resources arrive: rather than the costly overhead of constantly polling the server for new records; the client can just sit and wait to be notified of changes.

For example, suppose a Subscription is created with the following values:

{
  "resourceType": "Subscription",
  "status": "requested",
  "criteria": "Patient?name=smith",
  "channel": {
    "type": "websocket"
  }
}

To be notified of matches to this subscription (i.e. all new and updated Patients with the name “Smith”), you would create a websocket client and attach it to the endpoint you specified in the Websocket Subscription module. For example, if your Websocket Subscription is configured with a port of 9333 and a Context Path of /websocket, then if you are connecting locally, your websocket client would connect to ws://localhost:9333/websocket. After connecting, the websocket client sends a bind :id message to the server where :id corresponds to the id of the subscriptiuon your client needs to be notified about. If the bind is successful, the server will send back bound :id. From then on, whenever a match occurs, the server sends the message ping :id to the websocket client. Typically, the client would then go back to the FHIR Endpoint and retrieve the resource(s) it is interested in. A client can use the :lastUpdated parameter if it only wants to catch-up to new matching resources that arrived since the last time it queried the server.

5.8.8 Other Channel Types

 

Other channel types (e.g. sms) are not yet supported. Please let us know if you would like to use one of them.

5.8.9 Manually Triggering Subscriptions

 
Re-Triggering Subscriptions requires the FHIR_TRIGGER_SUBSCRIPTION permission.

It is sometimes desirable to have a Subscription “trigger” for a specific resource. This concept is easiest to explain with an example:

Suppose a REST HOOK subscription Subscription/123 exists with criteria Observation?status=complete. When an Observation is created with the given status, the subscription will deliver a notification to its intended target. Now suppose you want to manually cause your Observation to be processed a second time (and ultimately re-delivered, assuming it still matches the Subscription criteria) without modifying the Observation.

The $trigger-subscription operation can be useful in order to cause this subsequent “triggering” to occur.

Triggering a Resource by ID

In order to cause Observation/6 to be triggered for Subscription/123, the following Parameters resource should be crafted as an operation parameter. Multiple IDs may be specified using multiple parameter repetitions.

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "resourceId",
      "valueUri": "Observation/6"
    }
  ]
}

Triggering a Larger Set of Resources

Instead of individual resource IDs, a search URL may be used to request a group of resources to be triggered.

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "searchUrl",
      "valueString": "Observation?date=2018-10-16"
    }
  ]
}

Executing

This Parameters resource is then POSTed to the FHIR endpoint via a URL such as the following (the following triggers the resource for the subscription Subscription/123):
http://localhost:8000/Subscription/123/$trigger-subscription

This Parameters resource may also be POSTed to the Subscription type (instead of an individual Subscription) in order to trigger the resource to all active Subscriptions for which it matches:
http://localhost:8000/Subscription/$trigger-subscription

5.8.10 Subscription Activation

 

Subscription activation always happens in the FHIR Storage module. Only subscription types which have been enabled on that FHIR Storage module (e.g. REST_HOOK, EMAIL) will be activated. All other incoming subscriptions will be left in the REQUESTED state. The Subscription Matcher module only matches incoming resources against ACTIVE subscriptions.

5.8.11 In-Memory Matching

 

Smile CDR tries to match subscription criteria against an incoming resource using only the data available within the resource. If this isn’t possible, it queries the database to determine a match, either via a database call. The following subscription criteria elements are not supported by the in-memory matcher:

If any of these elements is present in a subscription criteria, then Smile CDR will not match the resource against the subscription criteria in-memory, but instead fall back to querying the repository directly.

5.8.12 Replication Mode

 

In replication mode, Smile CDR assumes that it is replicating all matching resources from one Smile CDR instance to another Smile CDR instance. This allows the server to make assumptions about how operations may be batched and sequenced. The following Subscription example shows a replication subscription:

{
  "resourceType": "Subscription",
  "status": "active",
  "reason": "Monitor new neonatal function (note, age will be determined by the monitor)",
  "criteria": "Observation?code=SNOMED-CT|1000000050",
  "channel": {
    "extension": [
      {
        "url": "https://smilecdr.com/fhir/ns/StructureDefinition/subscription-channel-rest-replicate-mode",
        "valueBoolean": true
      },
      {
        "url": "https://smilecdr.com/fhir/ns/StructureDefinition/subscription-channel-rest-replicate-id-prefix",
        "valueString": "CDR1-"
      }
    ],
    "type": "rest-hook",
    "endpoint": "http://localhost:40243/fhir/context",
    "payload": "application/json"
  }
}

When running in replication mode, the following extensions may be used.

NameURLTypeDescription
Enable Replication Mode boolean If set to true, this subscription will be delivered in replication mode.
Add ID Prefixes string If specified, all resource IDs will be prefixed with the given prefix (both in IDs, and in references to those IDs from other resources). This is useful in situations where you want to be able to replicate data from several source CDR instances to one target CDR instance, or in cases where the source CDR instance is using numeric IDs (since Smile CDR will not allow client-provided numeric IDs).
Strip ID Prefixes string If specified, all resource IDs will have the specified prefix removed from any resource IDs and references that are found within resources being delivered. This is helpful when subscriptions need to be delivered from a server that uses prefixes to a server that does not. For example, this could be used when delivering resources from a server that was populated by a subscription that uses the Add ID Prefixes on its own Subscription.
Strip ID Base URLs string If specified, all references containing this base URL will have the base URL removed prior to delivery.

If the replication is being sent to a second Smile CDR instance, you may wish to enable the dao_config.auto_create_placeholder_reference_targets.enabled configuration property on the replication target persistence module. This means that if payloads are received out-of-order by the receiving system, no payload will be rejected simply because it contains a reference to a resource that has not yet been received.

5.8.13 Strip Version IDs

 

By default, the resource version ID is included when delivering a resource to the REST HOOK receiving endpoint. This can be disabled using an extension on the REST HOOK channel.

{
  "resourceType": "Subscription",
  "status": "active",
  "reason": "Monitor new neonatal function (note, age will be determined by the monitor)",
  "criteria": "Observation?code=SNOMED-CT|1000000050",
  "channel": {
    "extension": [
      {
        "url": "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-strip-version-ids",
        "valueBoolean": true
      }
    ],
    "type": "rest-hook",
    "endpoint": "http://localhost:40243/fhir/context",
    "payload": "application/json"
  }
}

Note the following extension:

URLTypeDescription
http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-strip-version-ids boolean If set to true, the deliverer will strip the resource version ID before sending the resource

5.8.14 Delivering Latest Version

 

By default if a resource is created and then updated in rapid succession, the deliverer will deliver versions 1 and 2 to the REST HOOK target.

This can be adjusted so that if the resource gets updated before the deliverer manages to transmit the resource, only the latest version will be transmitted (and it may be delivered more than once).

{
  "resourceType": "Subscription",
  "status": "active",
  "reason": "Monitor new neonatal function (note, age will be determined by the monitor)",
  "criteria": "Observation?code=SNOMED-CT|1000000050",
  "channel": {
    "extension": [
      {
        "url": "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-deliver-latest-version",
        "valueBoolean": true
      }
    ],
    "type": "rest-hook",
    "endpoint": "http://localhost:40243/fhir/context",
    "payload": "application/json"
  }
}

Note the following extension:

URLTypeDescription
http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-deliver-latest-version boolean If set to true, the deliverer will attempt to send only the latest version of a resource.

5.8.15 Custom Delivery Class

 

Users may also supply a custom Java class that will be used to deliver the resource instead of the built-in FHIR client. This is useful in cases where you want to customize the delivery (e.g. by adding special attributes or filters, or by using another protocol altogether instead of FHIR/REST).

The following example shows a subscription that uses a custom delivery class:

{
  "resourceType": "Subscription",
  "id": "1",
  "meta": {
    "versionId": "1",
    "lastUpdated": "2017-08-19T16:52:11.041-04:00"
  },
  "status": "active",
  "reason": "Monitor new neonatal function (note, age will be determined by the monitor)",
  "criteria": "Observation?code=SNOMED-CT|1000000050&_format=xml",
  "channel": {
    "extension": [
      {
        "url": "https://smilecdr.com/fhir/ns/StructureDefinition/subscription-channel-rest-delivery-class",
        "valueString": "org.example.MyDeliverer"
      }
    ],
    "type": "rest-hook",
    "endpoint": "http://localhost:40243/fhir/context",
    "payload": "application/json",
    "header": [
      "Authorization: Bearer 123"
    ]
  }
}

Note the following extension:

URLTypeDescription
https://smilecdr.com/fhir/ns/StructureDefinition/subscription-channel-rest-delivery-class string This is the class name for the Java class that implements the ISubscriptionDeliverer interface.

The following example shows a simple delivery class:

public class ExampleDeliverer implements ISubscriptionDeliverer {

	private Logger ourLog = LoggerFactory.getLogger(ExampleDeliverer.class);

	/**
	 * Deliver the resource
	 *
	 * @param theOperation    The operation triggering this delivery
	 * @param theSubscription The subscription
	 * @param theResource     The resource
	 */
	@Override
	public void deliver(RestOperationTypeEnum theOperation, IBaseResource theSubscription, IBaseResource theResource) {

		// We will just log the resource; however, we would probably send it somewhere in a real scenario
		String id = theResource.getIdElement().getValue();
		ourLog.info("REST delivery type {} for resource {}", theOperation, id);

	}

}

5.8.16 Troubleshooting

 

The Subscription Troubleshooting Log can be helpful in diagnosing issues relating to subscriptions.