Record Usage in the Trace Repository

 

The CMS trace repository stores each Patient Access API usage event as a FHIR Communication resource, which reporting clients can search by member, application, and date. A Camel module consumes the Patient Access API topic, maps each event with the Patient Access JQ transformer, and posts the result to the trace endpoint. Optionally, the module also looks up the member's name, member number, plan, and group, and the requesting application's registered name, and adds them to the Communication.

Prerequisites

Complete the shared configuration described in CMS Trace Configuration:

Smile CDR provides the Patient Access transformers, the shared wrap-bundle.jq transformer, and the enrichment processor used below.

Create the Module

Add a Camel module for Patient Access API usage events, for example with the module ID camel_patient_access:

module.camel_patient_access.type=CAMEL
module.camel_patient_access.config.routes.script.file=classpath:/config_seeding/transaction-log-processor-patient-access.xml
module.camel_patient_access.config.functions.script.file=classpath:/config_seeding/transaction-log-processor-patient-access.js
module.camel_patient_access.config.definitions.spring_context_config.class=cdr.prior.auth.pas.txlog.PatientAccessEnrichmentAppCtx
module.camel_patient_access.config.enable_transaction_log_on_all_routes=false
  • definitions.spring_context_config.class – Provides the patientAccessEnrichmentProcessor bean used by the route. See Enrich the Communication.

See Create the Module for a description of the other properties.

Define the Routes

Save the following as smilecdr/classes/config_seeding/transaction-log-processor-patient-access.xml. The topic is the broker.channel_name of the Patient Access Transaction Log Broker module, and the URL is the base URL of the trace endpoint.

<routes xmlns="http://camel.apache.org/schema/spring">
  <route>
    <from uri="smile:clustermgr/broker?topic=channel_patient_access_txlog"/>
    <convertBodyTo type="java.lang.String"/>
    <to uri="bean:patientAccessEnrichmentProcessor"/>
    <doTry>
      <transform>
        <jq>resource:classpath:/txlogmap/patient-access/fhir-request/transformer.jq</jq>
      </transform>
      <doCatch>
        <exception>java.lang.Exception</exception>
        <log logName="ca.cdr.camel.txlog" message="Error in JQ transform: ${exception.message}" loggingLevel="ERROR"/>
        <setProperty name="errorMessage">
          <simple>${exception.message}</simple>
        </setProperty>
        <transform>
          <jq>resource:classpath:/txlogmap/patient-access/handle-error/transformer.jq</jq>
        </transform>
      </doCatch>
    </doTry>
    <transform>
      <jq>resource:classpath:/txlogmap/wrap-bundle.jq</jq>
    </transform>
    <to uri="direct:postPatientAccessCommunicationResource"/>
  </route>

  <route>
    <from uri="direct:postPatientAccessCommunicationResource"/>
    <convertBodyTo type="java.lang.String"/>
    <setHeader name="CamelHttpMethod">
      <constant>POST</constant>
    </setHeader>
    <setHeader name="CamelHttpPath">
      <constant></constant>
    </setHeader>
    <setHeader name="Content-Type">
      <constant>application/json</constant>
    </setHeader>
    <to uri="smile:camel/script?function=setBasicAuthHeaderTXLOG&amp;includeExchange=true"/>
    <to uri="http://localhost:8008/trace-fhir?bridgeEndpoint=true"/>
  </route>
</routes>
  • bean:patientAccessEnrichmentProcessor – Looks up the member and application details and stores them as exchange properties for the transformer. Remove this step, and the spring_context_config.class property, to map events from the access token claims alone.
  • txlogmap/patient-access/fhir-request/transformer.jq – The Patient Access transformer, provided by Smile CDR. See Usage Communication Format for its output.
  • txlogmap/patient-access/handle-error/transformer.jq – The Patient Access error transformer, provided by Smile CDR. It tags the Communication with patientaccess and error.

The Transaction Log Broker module's event_whitelist only admits FHIR_REQUEST events from the Patient Access API endpoint, so every event uses the same transformer and the route does not need a transformer selection function.

Define the Functions

Save the setBasicAuthHeaderTXLOG function as smilecdr/classes/config_seeding/transaction-log-processor-patient-access.js, and set the JS_SYSTEM_USER_PASSWORD environment variable to the system user's password.

Enrich the Communication

The transformer reads the member's Patient ID, the member's user identifier, and the application's client ID from the patient_id, subject, and client_id values that the transaction log interceptor adds to additionalJson. These values identify the member and application, but not by name. The patientAccessEnrichmentProcessor bean looks up the following details in the payer's FHIR repository, and the transformer adds them to the Communication:

Detail Looked Up From Added To the Communication As
Member name and member number Patient/[patient_id]. Only the name and the identifiers with the type MB are copied. A contained Patient with the ID member, referenced by subject.
Plan and group The member's active Coverage: the class values with the type plan and group. Identifiers with the systems .../communication/planid and .../communication/groupid.
Application name The Organization with the identifier https://smiledigitalhealth.com/credentialmap/oidc/client|[client_id]. When several match, the one that is not partOf another Organization is used. A contained Organization with the ID requester, referenced by recipient.

To register an application's name, add the client ID as an identifier to the application's Organization resource in the payer's FHIR repository:

{
  "resourceType": "Organization",
  "identifier": [
    {
      "system": "https://smiledigitalhealth.com/credentialmap/oidc/client",
      "value": "example-health-app"
    }
  ],
  "name": "Example Health App"
}

Configure the Lookups

The processor reads the payer's FHIR repository through a FHIR endpoint, for example the endpoint of the FHIR Storage module that holds member data. Set the following environment variables on every Smile CDR node that runs the Camel module:

  • PATIENT_ACCESS_ENRICHMENT_FHIR_BASE_URL – The base URL of the FHIR endpoint, for example https://example.com/payer-fhir. Use an https URL: the processor sends its credentials with HTTP Basic authentication, and logs a warning when the URL does not use TLS.
  • PATIENT_ACCESS_ENRICHMENT_FHIR_USERNAME and PATIENT_ACCESS_ENRICHMENT_FHIR_PASSWORD – The credentials of a user of that endpoint.

Grant the user read access to the looked-up resource types only:

Permission Argument
ROLE_FHIR_CLIENT
FHIR_READ_ALL_OF_TYPE Patient
FHIR_READ_ALL_OF_TYPE Coverage
FHIR_READ_ALL_OF_TYPE Organization

The FHIR endpoint must accept HTTP Basic authentication for this user.

Lookup Behavior

  • The lookups never prevent an event from being traced. When a lookup fails, or a resource is not found, the corresponding detail is left out and the Communication falls back to the access token claims. For example, recipient.display holds the client ID when no Organization is found.
  • When any of the three environment variables is missing, the processor logs a warning that names the missing variables at startup, and every event is mapped from the access token claims alone.
  • The details found for each pair of Patient ID and client ID are cached for 10 minutes. A new Coverage or a renamed Organization appears in new Communication resources within that time.

Start the Module

Restart Smile CDR to start the module. Each Patient Access API request now produces a Communication resource in the trace repository, in the format described in Usage Communication Format.

The Patient Access Transaction Log Broker module does not publish request or response bodies (persist_bodies_in_clustermgr_db=false), so the Binary resources created for each event hold an empty JSON object. The Communication records which member and application made the request, but not which resources were returned.

If you changed the user data keys in the callback script, the provided transformer does not find the values. Keep the keys patient_id, client_id, and subject.