When building a bundle from legacy data, I (and others) wanted to be able to control whether or not the resources were generated with a FHIR Request Method of PUT instead of the hard coded POST. I have extended the two classes responsible for transforming SDA to FHIR in an Interoperability Production to accomodate a setting that lets the user control the Request Method.
First is the Busines Process class. This includes a new parameter exposed to the "Settings" tab in Interoperability called FHIRRequestMethod. This also needs to pass the FHIRRequestMethod property to the transform class method as a parameter.
Class Demo.FHIR.DTL.Util.HC.SDA.FHIR.ProcessV2 Extends HS.FHIR.DTL.Util.HC.SDA3.FHIR.Process
{
Parameter SETTINGS = "FHIRRequestMethod:Basic"
Property FHIRRequestMethod As %String(MAXLEN = 10) [ InitialExpression = "POST" ]
Method ProcessSDARequest(pSDAStream, pSessionApplication As %String, pSessionId As %String, pPatientResourceId As %String = "") As %Status
{
New %HSIncludeTimeZoneOffsets
Set %HSIncludeTimeZoneOffsets = 1
Set tSC = $$$OK
Try {
If '$Data(%healthshare($$$CurrentClass, "isInteropHost"))#10 {
$$$ThrowOnError(##class(HS.Director).OpenCurrentProduction(.tProdObj))
Set tClassName = ""
For i = 1:1:tProdObj.Items.Count() {
If tProdObj.Items.GetAt(i).Name = ..TargetConfigName {
Set tClassName = tProdObj.Items.GetAt(i).ClassName
Quit
}
}
Kill tProdObj
Set tIsInteropHost = 0
Set tRequiredHostBases("HS.FHIRServer.Interop.Operation") = ""
Set tRequiredHostBases("HS.FHIRServer.Interop.HTTPOperation") = ""
Set tHostBase = ""
For {
Set tHostBase = $Order(tRequiredHostBases(tHostBase))
If tHostBase="" Quit
If $ClassMethod(tClassName, "%IsA", tHostBase) {
Set tIsInteropHost = 1
Quit
}
}
Set %healthshare($$$CurrentClass, "isInteropHost") = tIsInteropHost
} Else {
Set tIsInteropHost = %healthshare($$$CurrentClass, "isInteropHost")
}
Do ..GetHostAndPort(.tHost, .tPort)
Set tLocalHostAndPort = tHost_$Select(tPort'="":":",1:"")_tPort
If ..FHIRFormat="JSON" {
Set tMessageContentType = "application/fhir+json"
} ElseIf ..FHIRFormat="XML" {
Set tMessageContentType = "application/fhir+xml"
}
Set tFHIRMetadataSetKey = $ZStrip($Piece(..FHIRMetadataSet, "/", 1), "<>W")
Set tSchema = ##class(HS.FHIRServer.Schema).LoadSchema(tFHIRMetadataSetKey)
If '..FormatFHIROutput {
Set tIndentChars = ""
Set tLineTerminator = ""
Set tFormatter = ""
} Else {
Set tIndentChars = $Char(9)
Set tLineTerminator = $Char(13,10)
Set tFormatter = ##class(%JSON.Formatter).%New()
Set tFormatter.IndentChars = tIndentChars
Set tFormatter.LineTerminator = tLineTerminator
}
#dim tTransformObj As HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR
Set tTransformObj = $ClassMethod(..TransformClass, "TransformStream", pSDAStream, "HS.SDA3.Container", tFHIRMetadataSetKey, pPatientResourceId, "", ..FHIRRequestMethod)
Set tBundleObj = tTransformObj.bundle
$$$HSTRACE("Bundle object", "tBundleObj", tBundleObj.%ToJSON())
If ..TransmissionMode="individual" {
For i = 0:1:tBundleObj.entry.%Size()-1 {
If tIsInteropHost {
Set tSC = ..CreateAndSendInteropMessage(tBundleObj.entry.%Get(i), tSchema, tMessageContentType, tFormatter, tIndentChars, tLineTerminator, pSessionApplication, pSessionId)
} Else {
Set tSC = ..CreateAndSendFHIRMessage(tBundleObj.entry.%Get(i), tSchema, tLocalHostAndPort, tMessageContentType, tFormatter, tIndentChars, tLineTerminator, pSessionApplication, pSessionId)
}
}
} Else {
If tIsInteropHost {
Set tSC = ..CreateAndSendInteropMessage(tBundleObj, tSchema, tMessageContentType, tFormatter, tIndentChars, tLineTerminator, pSessionApplication, pSessionId)
} Else {
Set tSC = ..CreateAndSendFHIRMessage(tBundleObj, tSchema, tLocalHostAndPort, tMessageContentType, tFormatter, tIndentChars, tLineTerminator, pSessionApplication, pSessionId)
}
}
} Catch eException {
Set tSC = eException.AsStatus()
}
Quit tSC
}
Storage Default
{
<Data name="ProcessV2DefaultData">
<Subscript>"ProcessV2"</Subscript>
<Value name="1">
<Value>FHIRRequestMethod</Value>
</Value>
</Data>
<DefaultData>ProcessV2DefaultData</DefaultData>
<Type>%Storage.Persistent</Type>
}
}
Second is the transformation class, for which we need to also add a new property to store the FHIRRequestMethod parameter. The value of FHIRRequestMethod comes from the Class Method call to ..TransformStream. Once this parameter from the Business Process is passed into ..TransformStream, I store it in the class property so that all methods of this transform class have access to the value.
Class Demo.FHIR.DTL.Util.API.Transform.SDA3ToFHIRV2 Extends HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR
{
Property FHIRRequestMethod As %String(MAXLEN = 10)
ClassMethod TransformStream(stream As %Stream.Object, SDAClassname As %String, fhirVersion As %String, patientId As %String = "", encounterId As %String = "", FHIRRequestMethod As %String) As HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR
{
set source = $classmethod(SDAClassname, "%New")
if SDAClassname = "HS.SDA3.Container" {
$$$ThrowOnError(source.InitializeXMLParse(stream, "SDA3"))
}
else {
$$$ThrowOnError(source.XMLImportSDAString(stream.Read(3700000)))
}
return ..TransformObject(source, fhirVersion, patientId, encounterId, FHIRRequestMethod)
}
ClassMethod TransformObject(source, fhirVersion As %String, patientId As %String = "", encounterId As %String = "", FHIRRequestMethod As %String) As HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR
{
set schema = ##class(HS.FHIRServer.Schema).LoadSchema(fhirVersion)
set transformer = ..%New(schema)
Set transformer.FHIRRequestMethod = FHIRRequestMethod
if source.%ClassName(1) = "HS.SDA3.Container" {
do transformer.TransformContainer(source, patientId)
}
else {
do transformer.TransformSDA(source, patientId, encounterId)
}
return transformer
}
Method AddResource(source As HS.SDA3.SuperClass, resource As %RegisteredObject = "", ByRef resourceJson As %DynamicObject = "") As HS.FHIR.DTL.vR4.Model.Base.Reference [ Internal ]
{
if '$isobject(resourceJson) {
set resourceJson = ##class(%DynamicObject).%FromJSON(resource.ToJSON())
}
try {
do ..%resourceValidator.ValidateResource(resourceJson)
} catch ex {
do ..HandleInvalidResource(resourceJson, ex)
return ""
}
set entry = ##class(%DynamicObject).%New()
set entry.request = ##class(%DynamicObject).%New()
set id = ..GetId(source, resourceJson)
if id '= "" {
set resourceJson.id = id
}
set sourceIdentifier = ""
if resourceJson.resourceType = "Encounter" {
set sourceIdentifier = source.EncounterNumber
}
elseif ((source.%Extends("HS.SDA3.SuperClass")) && (resourceJson.resourceType '= "Provenance")) {
set sourceIdentifier = source.ExternalId
}
if id = "" {
if (resourceJson.resourceType = "Patient") && (..%patientId '= "") {
set id = ..%patientId
}
elseif $get(..%resourceIds(resourceJson.resourceType)) '= "" {
set id = ..%resourceIds(resourceJson.resourceType)
}
elseif (sourceIdentifier '= "") && $data(..%resourceIds(resourceJson.resourceType, sourceIdentifier)) {
set id = ..%resourceIds(resourceJson.resourceType, sourceIdentifier)
}
if id '= "" {
set resource.id = id
set resourceJson.id = id
}
}
if resourceJson.id '= "" {
set id = resourceJson.id
set entry.fullUrl = $select(..GetBaseURL()'="":..GetBaseURL() _ "/", 1:"") _ resourceJson.resourceType _ "/" _ resourceJson.id
set entry.request.method = "PUT"
set entry.request.url = resourceJson.resourceType _ "/" _ resourceJson.id
}
else {
set id = $zconvert($system.Util.CreateGUID(), "L")
set entry.fullUrl = "urn:uuid:" _ id
set entry.request.method = ..FHIRRequestMethod
set entry.request.url = resourceJson.resourceType
}
if resourceJson.resourceType = "Patient" {
set ..%patientId = id
}
elseif sourceIdentifier '= "" {
set ..%resourceIds(resourceJson.resourceType, sourceIdentifier) = id
}
set duplicate = ..IsDuplicate(resourceJson, id)
if duplicate '= "" {
return duplicate
}
set ..%resourceIndex(resourceJson.resourceType, id) = resourceJson
set entry.resource = resourceJson
do ..%bundle.entry.%Push(entry)
return ..CreateReference(resourceJson.resourceType, id)
}
}
These classes are designed to be used in an Interoperability Production. The demonstration that highlights to base version of these classes can be found here:
Learning Services: Converting Legacy Data to HL7 FHIR R4 in InterSystems IRIS for Health & Github Repo for Legacy To FHIR Transformation Demo