MDM UI Tutorial - Smile CDR Documentation

Master Data Management User Interface (MDMUI) Premium

⚠️ This guide assumes that MDM is already installed and configured. It focuses on enabling a user-facing interface to review and manage potential matches and duplicates.

MDM v2.0 will require a license, effective May 2024 Release onwards. MDM UI requires MDM as a dependency.

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.

For a more detailed step-by-step walkthrough of the same setup, including creating data steward user accounts, see Setting Up the MDM Web UI for Data Stewards.

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).

MDM 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:

Setup

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.

Add Subscription Matcher module

Add MDM Module

Configure FHIR Storage R4 RDBMS (persistence) module:

Configure SMART outbound module (smart_auth):

Configure JSON Admin API (admin_json) endpoint

Configure FHIR REST endpoint

Configure OIDC client

Add MDM UI Module

Add External IdP (Optional - if using SMART outbaund without federated mode, skip to next step)

Use the MDM UI app

Post Authorization Text Script JS Script

The following permissions are required for proper MDM UI operation. Please see the full list at Smile CDR Roles & Permissions.


function onTokenGenerating(theUserSession, theAuthorizationRequestDetails) {
    var userName = theUserSession.username;
    Log.info('User Name : ' + userName);

// Allows writing AuditEvent resources, which are used to log user actions in MDM UI (e.g., match decisions)
    theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'AuditEvent');

// Allows writing Task resources, used by MDM UI to track review actions (e.g., POSSIBLE_MATCH or DUPLICATE review tasks)
    theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'Task');

// Grants access to the Admin JSON API, which MDM UI uses to fetch module configuration and metadata
    theUserSession.addAuthority('ACCESS_ADMIN_JSON');

// Allows updating user records via internal Admin UI (if applicable)
    theUserSession.addAuthority('UPDATE_USER');

// Allows viewing the list of users via internal Admin UI
    theUserSession.addAuthority('VIEW_USERS');

if (userName === 'DATA_STEWARD') {
        // Enables Data Steward mode in the MDM UI, allowing users to resolve MDM match links
        theUserSession.addAuthority('ROLE_MDMUI_DATASTEWARD_FHIR');

// Allows viewing the MDM module configuration (but not editing it)
        theUserSession.addAuthority('VIEW_MODULE_CONFIG_FOR_MODULE', 'Master/mdm');

// Adds a claim to the access token indicating the role of the user, used by the frontend UI
        theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'data_steward');

} else if (userName === 'ADMIN') {
        // Allows executing the $mdm-evaluate operation to re-run MDM matching logic on demand
        theUserSession.addAuthority('FHIR_EXTENDED_OPERATION_ON_SERVER', '$mdm-evaluate');

// Enables Admin mode in the MDM UI, which includes access to config pages and job visibility
        theUserSession.addAuthority('ROLE_MDMUI_ADMIN_FHIR');

// Grants full administrative privileges for the MDM module, including modifying match rules and scripts
        theUserSession.addAuthority('MODULE_ADMIN_FOR_MODULE', 'Master/mdm');

// Allows viewing all batch jobs in the system, such as match evaluation runs
        theUserSession.addAuthority('VIEW_BATCH_JOBS');

// Adds a claim to the access token indicating the role of the user, used by the frontend UI
        theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'admin');
    }
}

Copy

function onTokenGenerating(theUserSession, theAuthorizationRequestDetails, theClientDetails) {
    const userRole = theUserSession.getUserData('user_role');
    Log.info('User Role : ' + userRole);
    theAuthorizationRequestDetails.addAccessTokenClaim('user_role', userRole);
}

function onAuthenticateSuccess(theOutcome, theOutcomeFactory, theContext) {
    const userRole = theContext.getClaim('user_role');

if (userRole === 'data_steward') {
        addDataStewardAuthorities(theOutcome);
    } else if (userRole === 'admin') {
        addAdminAuthorities(theOutcome);
    }
    theOutcome.setUserData('user_role', userRole);

return theOutcome;
}

function addDataStewardAuthorities(outcome) {
    addCommonAuthorities(outcome);
    const readResourceTypes = ['CodeSystem'];
    const readAndWriteResourceTypes = ['AuditEvent', 'Task'];
    addReadAuthorities(outcome, readResourceTypes);
    addReadAndWriteAuthorities(outcome, readAndWriteResourceTypes);
}

function addAdminAuthorities(outcome) {
    addCommonAuthorities(outcome);
    const readAndWriteResourceTypes = ['AuditEvent', 'Task'];
    const readResourceTypes = ['SearchParameter', 'CodeSystem'];
    addReadAndWriteAuthorities(outcome, readAndWriteResourceTypes);
    addReadAuthorities(outcome, readResourceTypes);
    addSmileSpecificAuthorities(outcome);
}

function addCommonAuthorities(outcome) {
    const commonResourceTypes = ['Patient', 'Practitioner', 'StructureDefinition', 'Organization', 'PractitionerRole'];
    addReadAndWriteAuthorities(outcome, commonResourceTypes);
    addSmileSpecificAuthorities(outcome);
}

function addReadAndWriteAuthorities(outcome, resourceTypes) {
    resourceTypes.forEach(resourceType => {
        outcome.addAuthority('FHIR_READ_ALL_OF_TYPE', resourceType);
        outcome.addAuthority('FHIR_WRITE_ALL_OF_TYPE', resourceType);
    });
}

function addReadAuthorities(outcome, resourceTypes) {
    resourceTypes.forEach(resourceType => {
        outcome.addAuthority('FHIR_READ_ALL_OF_TYPE', resourceType);
    });
}

function addSmileSpecificAuthorities(outcome) {
    const smileSpecificAuthorities = [\
        'FHIR_OP_MDM_DUPLICATE_GOLDEN_RESOURCES',\
        'FHIR_OP_MDM_LINK_HISTORY',\
        'FHIR_OP_MDM_MERGE_GOLDEN_RESOURCES',\
        'FHIR_OP_MDM_NOT_DUPLICATE',\
        'FHIR_OP_MDM_QUERY_LINKS',\
        'FHIR_OP_MDM_UPDATE_LINK',\
        'ACCESS_ADMIN_JSON',\
        'CHANGE_OWN_DEFAULT_LAUNCH_CONTEXTS',\
        'VIEW_BATCH_JOBS',\
        'VIEW_MODULE_CONFIG',\
        'VIEW_USERS',\
    ];

smileSpecificAuthorities.forEach(authority => {
        outcome.addAuthority(authority);
    });
}

Copy

MDM Tutorial

MDM UI Tutorial

Tutorials

Introduction Integration Testing Tutorial Custom Operations Tutorial HL7 v2.x Ingestion Tutorial Federated OAuth2/OIDC Tutorial MDM Tutorial MDM UI Tutorial MDM First Golden Resource Tutorial MDM Advanced Rule Authoring Tutorial MDM Web UI Admin Setup Tutorial CDA Import Tutorial CDA Export Tutorial CDA Export - Custom Narrative Generation Tutorial CDA Import JSON example CDA Export Template JSON example Consent Tutorial CMS-0057-F IG Setup Tutorial

MDM First Golden Resource Tutorial