Smile CDR v2024.02.PRE
On this page:

22.6.1Master Data Management User Interface (MDMUI)
Experimental

 
This is a preview feature initially introduced in Smile CDR 2023.05. Feedback is welcome. Please get in touch to discuss if you are interested in using MDMUI during the preview of this new feature. Its license status may change in a future release.

MDMUI is a user-facing application designed to make it easy for administrators to accept or refute links created by MDM (Master Data Management). It includes the ability to search for a particular resource, view a list of all potential matches and potential duplicates, and assign tasks to users for workload management.

MDMUI currently supports FHIR R4 endpoints only.

NOTE: if you are using a reverse / web proxy, please see step 10 at the very bottom (last step).

22.6.1.1MDM UI Resources

MDM UI will create FHIR resources for its own workflow management, these will be tagged with the tag "https://smilecdr.com/fhir/ns/mdm/ui/NamingSystem/tags|MDM_UI". It will create new resources in the following scenarios:

  • A Task resource will be created to track each POSSIBLE_MATCH and POSSIBLE_DUPLICATE link created by MDM on the feed. This will be created by the MDM Interceptor mentioned in the Setup section below.
  • A Practitioner resource will be created for each User that logs in to represent the user responsible for linking/unlinking records.
  • A PractitionerRole will be created for each user to note if they are a data steward or an admin user (defaults to data steward for all new users).
  • An Organization will be created if it does not already exist with the identifier specified in the MDM UI module configuration, and the PractitionerRoles will be tied to that Organization.

22.6.2Setup
Experimental

 

MDM UI depends on having certain components already configured in Smile. Please follow these steps to configure a fresh Smile install to work with MDMUI.

  1. Add Subscription Matcher module
  • NOTE: On a fresh install, this is already available and configured - this step can be skipped
  • Add module of type Subscription Matcher (All FHIR Versions)
  • Select dependency FHIR Storage module as FHIR Storage (R4 RDBMS)
  • Create and start module
  1. Add MDM Module
  • Add module of type mdm
  • Set interceptor bean type to -- ca.cdr.endpoint.mdm.ui.interceptor.MDMInterceptorContext --. Note: This is not required starting Smile CDR release v2023.11.
  • Set scripts for matching and survivorship -- NOTE: this is not required for setting up MDM, but is required for creating Golden records and match rules
  • Select dependency Subscription Matcher from step 1 above
  • Start module
  1. Configure FHIR Storage R4 RDBMS (persistence) module:
  • Enable "MDM mode" (this will allow MDM module to work)
  • Enable "Message Subscription" (this will allow the Post-MDM Task Interceptor added in interceptor bean type in step 2 to work, adding Task resources to Matches for the UI)
  • Note: Starting Smile CDR v2024.02, enable Seed Base Validation resources. This will seed resources like StructureDefinition, which will be used by the front application.
  • Restart persistence
  1. Configure smart outbound module (smart_auth):
  • Disable “Enforce approved scopes to restrict permissions”
  • Enable CORS to future MDMUI app port (cannot use * here due to logout URL constraints) (ie. http://localhost:8080)
  • For ease of use and to differentiate paths, recommended to change:
    • HTTP Listener > Context Path -- change to "/smartauth"
    • OpenID Connect (OIDC) > Issuer URL -- add "/smartauth" to the end
  • Restart module
  1. Configure JSON Admin API (admin_json) endpoint
  • For ease of use and to differentiate paths, recommended to change:
    • HTTP Listener > Context Path -- change to "/json-admin"
  • Enable CORS to * or MDMUI app port (ie. http://localhost:8080)
  • Enable OpenID Connect Security
  • Add dependency for OpenID Connect Authentication to smart_auth
  • Restart module
  1. Configure FHIR REST endpoint
  • Add module FHIR REST Endpoint (All FHIR Versions) - NOTE: On a fresh install, this is already available and configured - this step can be skipped
  • For ease of use and to differentiate paths, recommended to change:
    • HTTP Listener > Context Path -- change to "/fhir-request"
  • Set value for Fixed Value for Endpoint Base URL to the externally visible URL for this endpoint ( e.g. http://localhost:8000/fhir-request or https://mydomain.com/fhir-request/)
  • Under HTTP Listener configure a Listener Port (any port not in use e.g. 8000) - NOTE: On a fresh install, this is already available and configured - this step can be skipped
  • Enable OpenID Connect Security
  • Enable CORS to * or MDM UI port (ie. http://localhost:8080)
  • Add dependency for OpenID Connect Authentication to smart_auth
  • Add dependency for FHIR Storage Module (any FHIR version) to persistence (FHIR Storage (R4 RDBMS))
  • Start module
  1. Configure OIDC client
  • Under Users & Authorization OpenID Connect Clients, use the "Add Client" button to add a new OIDC client
  • Enter a Client ID and Client Name of your choice (e.g. "MDM_UI")
  • Select Authorized Grant Types: Authorization Code, Refresh Token, and JWT Bearer Token
  • Set Authorized Redirect URLs to the base URL for your new MDM UI instance, e.g. http://localhost:8080/ or other port/context path of your choice (NOTE: this requires the context path as well, if one has been set -- http://localhost:8080/mdm-ui/ -- this context path can be set in step 8 when setting up MDM UI Module)
  • Set Scopes to: cdr_all_user_authorities launch/patient launch/practitioner offline_access openid profile
  • Create Client to create the client
  1. Add MDM UI Module
  • Add module of type MDM UI
  • Set Organization Identifier - a new Organization resource will be created if one does not exist with the given system|identifier
  • Set JSON admin URL to the public facing URL for accessing the JSON admin API (remember to add context path if set in step 5)
  • Set OIDC client ID (should match Client ID of OIDC client created in step 7), issuer url (this is your smartauth URL, remember to set /smartauth if this was the context path set), Redirect URL (this should include the context path that will be set in HTTP Listener below, ie. http://localhost:8080/mdm-ui/ - this is also set in OIDC Client), logout url (this is your smartauth URL, remember to set /smartauth if this was the context path set) for smart auth
  • Set HTTP Listener Port (e.g. 8080) - should match the redirect URL of the OIDC client and any CORS settings configured above -- Recommended to also set context path (ie. /mdm-ui/) NOTE: this should match the redirect URL above, and the OIDC Client Authorized Redirect URLS saved
  • Enable Allow Anonymous Access - the MDM UI app provides its own OpenID Connect authentication
  • Select all dependencies
  • Start module
  1. Add Custom Search Parameters:
  • Note: Starting Smile CDR v2023.11.R01, this SearchParameter resources will not be seeded and are not required as MDMUI will be using AuditEvent resources.
  • Note: Starting Smile CDR v2023.08.R01, this step is not required. SearchParameter resources will be automatically loaded.
  • POST the following bundle of Search Parameter resources to FHIR endpoint supporting FHIR version R4 and above:
{
   "resourceType": "Bundle",
   "type": "transaction",
   "entry": [
      {
         "fullUrl": "SearchParameter/Task-relevant-history",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-relevant-history",
            "url": "https://smilecdr.com/fhir/ns/mdm/ui/SearchParameter/Task-relevant-history",
            "name": "Task relevant history",
            "status": "active",
            "description": "Search by Provenance records relating to this Task.",
            "code": "relevant-history",
            "base": [
               "Task"
            ],
            "type": "reference",
            "expression": "Task.relevantHistory",
            "target": [
               "Provenance"
            ]
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=relevant-history&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-restriction-recipient",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-restriction-recipient",
            "url": "https://smilecdr.com/fhir/ns/mdm/ui/SearchParameter/Task-restriction-recipient",
            "name": "Task restriction recipient",
            "status": "active",
            "description": "Search by task restriction recipient reference.",
            "code": "restriction-recipient",
            "base": [
               "Task"
            ],
            "type": "reference",
            "expression": "Task.restriction.recipient",
            "target": [
               "Patient",
               "Practitioner",
               "PractitionerRole",
               "RelatedPerson",
               "Group",
               "Organization"
            ]
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=restriction-recipient&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-owner-or-restriction-recipient",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-owner-or-restriction-recipient",
            "url": "https://smilecdr.com/fhir/ns/mdm/ui/SearchParameter/Task-owner-or-restriction-recipient",
            "name": "Task owner or restriction recipient",
            "status": "active",
            "description": "Search by task owner or restriction recipient reference.",
            "code": "owner-or-recipient",
            "base": [
               "Task"
            ],
            "type": "reference",
            "expression": "Task.owner | Task.restriction.recipient",
            "target": [
               "CareTeam",
               "Device",
               "Group",
               "HealthcareService",
               "Organization",
               "Patient",
               "Practitioner",
               "PractitionerRole",
               "RelatedPerson"
            ]
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=owner-or-recipient&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-restriction-recipient-exists",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-restriction-recipient-exists",
            "url": "https://smilecdr.com/fhir/ns/mdm/ui/SearchParameter/Task-restriction-recipient-exists",
            "name": "Task restriction recipient if exists",
            "status": "active",
            "description": "A server match on whether Task recipient exists, alternative to using the :missing operator.",
            "code": "task-recipient-exists",
            "base": [
               "Task"
            ],
            "type": "string",
            "expression": "Task.restriction.recipient.exists()"
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=task-recipient-exists&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-mdmui-identifier-system",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-mdmui-identifier-system",
            "url": "https://smilecdr.com/fhir/ns/StructureDefinition/Task-mdmui-identifier-system",
            "name": "Task mdmui identifier system",
            "status": "active",
            "description": "Search by Task identifier using MDMUI system.",
            "code": "task-mdmui-identifier-system",
            "base": [
               "Task"
            ],
            "type": "string",
            "expression": "Task.identifier.where(system='https://smilecdr.com/fhir/ns/mdm/ui/Task/identifier').value"
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=task-mdmui-identifier-system&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-mdmui-identifier-system-unique",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-mdmui-identifier-system-unique",
            "extension": [
               {
                  "url": "http://hapifhir.io/fhir/StructureDefinition/sp-unique",
                  "valueBoolean": true
               }
            ],
            "url": "https://smilecdr.com/fhir/ns/StructureDefinition/Task-mdmui-identifier-system-unique",
            "name": "Task mdmui identifier system unique",
            "status": "active",
            "description": "Enforcing uniqueness on Task identifier using MDMUI system.",
            "code": "task-mdmui-identifier-system-unique",
            "base": [
               "Task"
            ],
            "type": "composite",
            "expression": "Task.identifier.where(system='https://smilecdr.com/fhir/ns/mdm/ui/Task/identifier').value",
            "component": [
               {
                  "definition": "SearchParameter/Task-mdmui-identifier-system",
                  "expression": "Task"
               }
            ]
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=task-mdmui-identifier-system-unique&status=active"
         }
      }
   ]
}
  • POST the following bundle of Search Parameter resources to FHIR endpoint supporting version R3
  • Note: MDMUI front-end doesn't support FHIR version STU3 yet. This is for future usage.
{
   "resourceType": "Bundle",
   "type": "transaction",
   "entry": [
      {
         "fullUrl": "SearchParameter/Task-relevant-history",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-relevant-history",
            "url": "https://smilecdr.com/fhir/ns/mdm/ui/SearchParameter/Task-relevant-history",
            "name": "Task relevant history",
            "status": "active",
            "code": "relevant-history",
            "base": [
               "Task"
            ],
            "type": "reference",
            "description": "Search by Provenance records relating to this Task.",
            "expression": "Task.relevantHistory",
            "target": [
               "Provenance"
            ]
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=relevant-history&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-restriction-recipient",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-restriction-recipient",
            "url": "https://smilecdr.com/fhir/ns/mdm/ui/SearchParameter/Task-restriction-recipient",
            "name": "Task restriction recipient",
            "status": "active",
            "code": "restriction-recipient",
            "base": [
               "Task"
            ],
            "type": "reference",
            "description": "Search by task restriction recipient reference.",
            "expression": "Task.restriction.recipient",
            "target": [
               "Patient",
               "Practitioner",
               "PractitionerRole",
               "RelatedPerson",
               "Group",
               "Organization"
            ]
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=restriction-recipient&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-owner-or-restriction-recipient",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-owner-or-restriction-recipient",
            "url": "https://smilecdr.com/fhir/ns/mdm/ui/SearchParameter/Task-owner-or-restriction-recipient",
            "name": "Task owner or restriction recipient",
            "status": "active",
            "code": "owner-or-recipient",
            "base": [
               "Task"
            ],
            "type": "reference",
            "description": "Search by task owner or restriction recipient reference.",
            "expression": "Task.owner | Task.restriction.recipient",
            "target": [
               "CareTeam",
               "Device",
               "Group",
               "HealthcareService",
               "Organization",
               "Patient",
               "Practitioner",
               "PractitionerRole",
               "RelatedPerson"
            ]
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=owner-or-recipient&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-restriction-recipient-exists",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-restriction-recipient-exists",
            "url": "https://smilecdr.com/fhir/ns/mdm/ui/SearchParameter/Task-restriction-recipient-exists",
            "name": "Task restriction recipient if exists",
            "status": "active",
            "code": "task-recipient-exists",
            "base": [
               "Task"
            ],
            "type": "string",
            "description": "A server match on whether Task recipient exists, alternative to using the :missing operator.",
            "expression": "Task.restriction.recipient.exists()"
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=task-recipient-exists&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-mdmui-identifier-system",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-mdmui-identifier-system",
            "url": "https://smilecdr.com/fhir/ns/StructureDefinition/Task-mdmui-identifier-system",
            "name": "Task mdmui identifier system",
            "status": "active",
            "code": "task-mdmui-identifier-system",
            "base": [
               "Task"
            ],
            "type": "string",
            "description": "Search by Task identifier using MDMUI system.",
            "expression": "Task.identifier.where(system='https://smilecdr.com/fhir/ns/mdm/ui/Task/identifier').value"
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=task-mdmui-identifier-system&status=active"
         }
      },
      {
         "fullUrl": "SearchParameter/Task-mdmui-identifier-system-unique",
         "resource": {
            "resourceType": "SearchParameter",
            "id": "Task-mdmui-identifier-system-unique",
            "extension": [
               {
                  "url": "http://hapifhir.io/fhir/StructureDefinition/sp-unique",
                  "valueBoolean": true
               }
            ],
            "url": "https://smilecdr.com/fhir/ns/StructureDefinition/Task-mdmui-identifier-system-unique",
            "name": "Task mdmui identifier system unique",
            "status": "active",
            "code": "task-mdmui-identifier-system-unique",
            "base": [
               "Task"
            ],
            "type": "composite",
            "description": "Enforcing uniqueness on Task identifier using MDMUI system.",
            "expression": "Task.identifier.where(system='https://smilecdr.com/fhir/ns/mdm/ui/Task/identifier').value",
            "component": [
               {
                  "definition": {
                     "reference": "SearchParameter/Task-mdmui-identifier-system"
                  },
                  "expression": "Task"
               }
            ]
         },
         "request": {
            "method": "PUT",
            "url": "SearchParameter?base=Task&code=task-mdmui-identifier-system-unique&status=active"
         }
      }
   ]
}
  1. Use the MDM UI app
  • You should now be ready to view MDM UI at the port and context path specified in step 7 above! ( e.g. http://localhost:8080/mdm_ui/)
  • If you are using a web proxy to expose ports externally you will need to configure the following endpoints for external access:
    • MDM UI port and context path
    • FHIR endpoint
    • Smart outbound security module
    • JSON Admin API
  • You will also need to set in Smart_auth module
    • HTTP Listener
    • Enable "Respect Forward Headers"
    • Enable "HTTPS Forwarding Assume"