-
Notifications
You must be signed in to change notification settings - Fork 179
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #552 from microsoft/personal/duburson/convert-api-…
…docs Initial documentation release FHIR Converter API *Preview* - Update main readme for new updated container based convert. - Readme updated with previous version section, which directs to prior versions. - Create concept documents for resource id generation and validation. - Add example using built in date filter to Filter and Tags documentation. - Remove no longer relevant TempleteManagementCLI.md from main.
- Loading branch information
Showing
47 changed files
with
3,975 additions
and
293 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Resource ID generation | ||
|
||
The default templates provided with the Converter computes Resource IDs using the input data fields. In order to preserve the generated Resource IDs, the default templates provide create **PUT requests**, instead of POST requests in the generated bundles. | ||
|
||
For **HL7v2 to FHIR conversion**, [HL7v2 DotLiquid templates](data/Templates/Hl7v2/ID) help generate FHIR resource IDs from HL7v2 messages. An ID generation template does three things: 1) extract identifiers from the input segment or field; 2) combine the identifers with resource type and base ID (optional) as hash seed; 3) compute hash as output ID. | ||
|
||
For **C-CDA to FHIR conversion**, [C-CDA DotLiquid templates](data/Templates/Ccda/Utils) generate FHIR resource IDs in two ways: 1) [ID generation template](data/Templates/Ccda/Utils/_GenerateId.liquid) helps generate Patient ID and Practitioner ID; 2) the resource IDs for other resources are generated from the resource object directly. | ||
|
||
For **JSON to FHIR conversion**, there is no standardized JSON input message types unlike HL7v2 messages or C-CDA documents. Therefore, instead of default templates we provide you with some sample JSON DotLiquid templates that you can use as a starting guide for your custom JSON conversion templates. You can decide how to generate the resource IDs according to your own inputs, and use our sample templates as a reference. | ||
|
||
For **FHIR STU3 to R4 conversion**, the Resource ID from STU3 resource is copied over to corresponding R4 resource. | ||
|
||
The Converter introduces a concept of "base resource/base ID". Base resources are independent entities, like Patient, Organization, Device, etc, whose IDs are defined as base ID. Base IDs could be used to generate IDs for other resources that relate to them. It helps enrich the input for hash and thus reduce ID collision. | ||
For example, a Patient ID is used as part of hash input for an AllergyIntolerance ID, as this resource is closely related with a specific patient. | ||
|
||
Below is an example where an AllergyIntolerance ID is generated, using ID/AllergyIntolerance template, AL1 segment and patient ID as its base ID. | ||
The syntax is `{% evaluate [id] using [template] [variables] -%}`. | ||
|
||
```liquid | ||
{% evaluate allergyIntoleranceId using 'ID/AllergyIntolerance' AL1: al1Segment, baseId: patientId -%} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# Resource validation and post-processing | ||
|
||
The output of converter depends on the templates as well as the quality and richness of input messages. Therefore, it is important that you review and validate the Converter output before using those in production. | ||
|
||
In general, you can use [HL7 FHIR validator](https://wiki.hl7.org/Using_the_FHIR_Validator) to validate a FHIR resource. You may be able to fix some of the conversion issues by appropriately changing the templates. For other issues, you may need to have a post-processing step in your pipeline. | ||
|
||
In some cases, due to lack of field level data in the incoming messages, the Converter may produce resources without useful information or even without ID. You can use `Hl7.Fhir.R4` .NET library to filter such resources in your pipeline. Here is the sample code for such purpose. | ||
|
||
```C# | ||
using Hl7.Fhir.Model; | ||
using Hl7.Fhir.Serialization; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
public class PostProcessor | ||
{ | ||
private readonly FhirJsonParser _parser = new FhirJsonParser(); | ||
|
||
public IEnumerable<Resource> FilterResources(IEnumerable<string> fhirResources) | ||
{ | ||
return fhirResources | ||
.Select(fhirResource => _parser.Parse<Resource>(fhirResource)) | ||
.Where(resource => !IsEmptyResource(resource)) | ||
.Where(resource => !IsIdAbsentResource(resource)); | ||
} | ||
|
||
public bool IsEmptyResource(Resource resource) | ||
{ | ||
try | ||
{ | ||
var fhirResource = resource.ToJObject(); | ||
var properties = fhirResource.Properties().Select(property => property.Name); | ||
// an empty resource contains no properties other than "resourceType" and "id" | ||
return !properties | ||
.Where(property => !property.Equals("resourceType")) | ||
.Where(property => !property.Equals("id")) | ||
.Any(); | ||
} | ||
catch (Exception e) | ||
{ | ||
Console.Error.WriteLine(e.Message); | ||
// deal with the exception... | ||
} | ||
|
||
return false; | ||
} | ||
|
||
public bool IsIdAbsentResource(Resource resource) | ||
{ | ||
try | ||
{ | ||
return string.IsNullOrWhiteSpace(resource.Id); | ||
} | ||
catch (Exception e) | ||
{ | ||
Console.Error.WriteLine(e.Message); | ||
// deal with the exception... | ||
} | ||
return false; | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
/* | ||
This template deploys the following: | ||
* Storage account (if enableTemplateStoreIntegration is set to true) | ||
* Storage account container (if enableTemplateStoreIntegration is set to true) | ||
* Key vault (if deployKeyVault is set to true) | ||
* User assigned identity with Key Vault Secrets User role on the Key Vault (if deployKeyVault is set to true) | ||
* Role assignment for the user assigned identity to access the Key Vault (if deployKeyVault is set to true) | ||
*/ | ||
|
||
@description('Location where the storage account is deployed. For list of Azure regions where Blob Storage is available, see [Products available by region](https://azure.microsoft.com/en-us/explore/global-infrastructure/products-by-region/?products=storage).') | ||
@allowed([ | ||
'australiacentral' | ||
'australiaeast' | ||
'australiasoutheast' | ||
'brazilsouth' | ||
'canadacentral' | ||
'canadaeast' | ||
'centralindia' | ||
'centralus' | ||
'chinaeast2' | ||
'chinanorth2' | ||
'chinanorth3' | ||
'eastasia' | ||
'eastus' | ||
'eastus2' | ||
'francecentral' | ||
'germanywestcentral' | ||
'italynorth' | ||
'japaneast' | ||
'japanwest' | ||
'koreacentral' | ||
'northcentralus' | ||
'northeurope' | ||
'norwayeast' | ||
'polandcentral' | ||
'qatarcentral' | ||
'southafricanorth' | ||
'southcentralus' | ||
'southeastasia' | ||
'southindia' | ||
'swedencentral' | ||
'switzerlandnorth' | ||
'uaenorth' | ||
'uksouth' | ||
'ukwest' | ||
'westcentralus' | ||
'westeurope' | ||
'westus' | ||
'westus2' | ||
'westus3' | ||
]) | ||
param location string | ||
|
||
@description('If set to true, a storage account and blob container will be deployed with the specified names for storing custom templates.') | ||
param deployTemplateStore bool | ||
|
||
@description('Name of the storage account to be deployed.') | ||
param templateStorageAccountName string = '' | ||
|
||
@description('Name of the storage account container to be deployed.') | ||
param templateStorageAccountContainerName string = '' | ||
|
||
@description('If set to true, a key vault and user assigned managed identity will be deployed with the specified names.') | ||
param deployKeyVault bool | ||
|
||
@description('Name of the key vault to be deployed.') | ||
param keyVaultName string = '' | ||
|
||
@description('Name of the user-assigned managed identity to be deployed for accessing the key vault.') | ||
param keyVaultUserAssignedIdentityName string = '' | ||
|
||
resource templateStorageAccountCreated 'Microsoft.Storage/storageAccounts@2022-09-01' = if (deployTemplateStore) { | ||
name: deployTemplateStore ? templateStorageAccountName : 'default' | ||
location: location | ||
sku: { | ||
name: 'Standard_LRS' | ||
} | ||
kind: 'StorageV2' | ||
properties: {} | ||
} | ||
|
||
resource templateStorageAccount 'Microsoft.Storage/storageAccounts/blobServices@2022-09-01' = if (deployTemplateStore) { | ||
name: 'default' | ||
parent: templateStorageAccountCreated | ||
} | ||
|
||
resource templateStorageAccountContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2022-09-01' = if (deployTemplateStore) { | ||
name: deployTemplateStore ? templateStorageAccountContainerName : 'default' | ||
parent: templateStorageAccount | ||
} | ||
|
||
resource keyVault 'Microsoft.KeyVault/vaults@2021-04-01-preview' = if (deployKeyVault) { | ||
name: deployKeyVault ? keyVaultName : 'default' | ||
location: location | ||
properties: { | ||
sku: { | ||
family: 'A' | ||
name: 'standard' | ||
} | ||
tenantId: subscription().tenantId | ||
enableRbacAuthorization: true | ||
} | ||
} | ||
|
||
resource keyVaultUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = if (deployKeyVault) { | ||
name: deployKeyVault ? keyVaultUserAssignedIdentityName : 'default' | ||
location: location | ||
} | ||
|
||
var kvSecretUserRole = '4633458b-17de-408a-b874-0445c86b69e6' // Key Vault Secrets User role | ||
resource keyVaultSecretsUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (deployKeyVault) { | ||
name: guid(resourceGroup().id, keyVaultUserAssignedIdentity.id, kvSecretUserRole) | ||
scope: keyVault | ||
properties: { | ||
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', kvSecretUserRole) | ||
principalId: deployKeyVault ? keyVaultUserAssignedIdentity.properties.principalId : 'default' | ||
principalType: 'ServicePrincipal' | ||
} | ||
} | ||
|
||
output templateStorageAccountName string = deployTemplateStore ? templateStorageAccountCreated.name : '' | ||
output templateStorageAccountContainerName string = deployTemplateStore ? templateStorageAccountContainer.name : '' | ||
output keyVaultName string = deployKeyVault ? keyVault.name : '' | ||
output keyVaultUAMIName string = deployKeyVault ? keyVaultUserAssignedIdentity.name : '' |
Oops, something went wrong.