Artículo
· 11 jul, 2023 Lectura de 8 min

Instalación y adaptación de EMPI en modo Standalone - Alimentando a la bestia con FHIR - Configuración

¡Volvemos al ataque con nuestro EMPI!

En artículos anteriores hemos visto como configurar y personalizar nuestro EMPI, hemos visto como podemos como podemos incluir nuevos pacientes en nuestro sistema mediante mensajería HL7, pero claro, ¡no todo es HL7 v.2 en esta vida! ¿Cómo podríamos configurar nuestra instancia del EMPI para trabajar con mensajería FHIR?

¿Qué es FHIR?

Para los que no estéis demasiado familiarizados con el término FHIR únicamente indicar que son las iniciales de Fast Healthcare Interoperability Resource. FHIR es un estándar de interoperabilidad sanitario desarrollado por HL7 en el que, basándose en el formato JSON y comunicaciones REST, se establecen una serie de "recursos" que portan diferentes tipos de información (desde datos del paciente, centros hospitalarios, diagnósticos, citas médicas...) podéis echar un vistazo a todos estos recursos en su página oficial 

InterSystems y FHIR

Desde InterSystems somos conscientes de la utilidad que FHIR proporciona para el mundo de la interoperabilidad sanitaria y por ello disponemos de una amplia gama de productos y funcionalidades que permiten aprovechar y exprimir todo el potencial del mismo:

Pues bien, para nuestro artículo vamos a aprovechar la funcionalidad que nos proporciona el Adapter para recibir mensajería FHIR.

Configuración del EMPI

Instalación del adaptador de FHIR

 Repasemos en primer lugar de qué servicios disponemos en nuestra instalación standalone del EMPI:

Cómo podéis observar, tras la instalación del EMPI en modo standalone se creó un servicio de Registry con todas las opciones necesariarias para configurar nuestro EMPI, así como un namespace que llamamos HSPIDATA y en el que disponemos de una producción para gestionar las funcionalidades de interoperabilidad que necesitamos.

Para nuestro caso hemos creado un nuevo namespace denominado WEBINAR en el que desplegaremos una producción de interoperabilidad normal y corriente. Será en este namespace donde instalaremos el FHIR Interoperability Adapter, esta instalación nos publicará una aplicación web que será a la que enviemos nuestras llamadas REST con los mensajes FHIR. Para ello ejecutaremos los siguientes comandos desde el terminal:

zn "WEBINAR"
set status = ##class(HS.FHIRServer.Installer).InteropAdapterConfig("/csp/healthshare/webinar/fhir/r4")

Una vez instalado comprobaremos desde el portal de gestión la nueva aplicación creada:

Comprobemos nuestra producción en el namespace WEBINAR:

Por defecto se han creado dos nuevos Business Components:

  1. InteropService: en nuestra producción le hemos cambiado el nombre a su clase HS.FHIRServer.Interop.Service que será el Business Service encargado de la recepción del mensaje FHIR remitido a nuestro endpoint /csp/healthshare/webinar/fhir/r4
  2. InteropOperation: que hemos cambiado a HS.FHIRServer.Interop.Operation. Para nuestro ejemplo hemos ignorado este Business Operation ya que vamos a enviar la información del mensaje FHIR vía TCP a nuestra producción del EMPI

Creación de Business Components para gestionar la mensajería FHIR

Muy bien, ya tenemos nuestro Business Service habilitado para recibir los mensajes FHIR, ahora lo que nos interesa es extraer la información de dicho mensaje para poder remitírla a nuestra producción del EMPI. Echemos un ojo al tipo de mensaje que va a recibir HS.FHIRServer.Interop.Service:

Include HS.FHIRServer

/// FHIRServer REST Business Service
Class HS.FHIRServer.Interop.Service Extends (Ens.BusinessService, HS.HC.Util.Trace.Helper)
{

Parameter SETTINGS = "TargetConfigName:Basic:selector?context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},Timeout:Basic";
/// Configuration item to which to send inbound messages.
Property TargetConfigName As Ens.DataType.ConfigName [ InitialExpression = "HS.FHIRServer.Interop.Operation" ];
/// Timeout for dispatch (so we don't hold up the HTTP service too long or hang up a production shutdown).
Property Timeout As %Integer [ InitialExpression = 25 ];
/// Process an incoming message into the production; dispatch it to the configured target.
/// The Interoperability contract requires that errors be returned as %Status here.
Method OnProcessInput(pRequest As HS.FHIRServer.Interop.Request, Output pResponse As HS.FHIRServer.Interop.Response) As %Status

El mensaje es del tipo HS.FHIRServer.Interop.Request y si consultamos la documentación veremos que lleva asociado una propiedad denominada QuickStreamId la cual utilizaremos para extraer el mensaje FHIR asociado. Para ello redirigiremos el mensaje recibido en el Business Service al Business Process Webinar.BP.FHIRToHubRequest

Este Business Process sólo tendrá como función la de redirigir el mensaje al Business Operation FromFHIRToMPI encargado del envío del mensaje vía TCP a la producción del EMPI.

Method OnRequest(pRequest As HS.FHIRServer.Interop.Request, Output pResponse As HS.FHIRServer.Interop.Response) As %Status
{
	Set tSC = ..SendRequestSync("FromFHIRToMPI", pRequest, .pResponse)
	Quit $$$OK
}

Echemos un vistazo a la clase Webinar.BO.ToMPI y expliquemos como obtiene el mensaje de FHIR y se transforma en un String que enviaremos a nuestra producción del EMPI:

Include HS.FHIRServer

Class Webinar.BO.ToMPI Extends Ens.BusinessOperation
{

Parameter ADAPTER = "EnsLib.TCP.CountedOutboundAdapter";
Property Adapter As EnsLib.TCP.CountedOutboundAdapter;
Parameter INVOCATION = "Queue";
Parameter SETTINGS = "FHIRMetadataSet::selector?context={HS.FHIRServer.Util.ContextSearch/FHIRMetadataSets}";
/// FHIR Metadata Set. These are defined in HS_FHIRServer.FHIRMetadataSet.
Property FHIRMetadataSet As %String(MAXLEN = 256);
Method SendFHIRRequest(pRequest As HS.FHIRServer.Interop.Request, Output pResponse As HS.FHIRServer.Interop.Response) As %Status
{
	// Get version of FHIR message configured
	Set tFHIRMetadataSetKey = $ZStrip($Piece(..FHIRMetadataSet, "/", 1), "<>W")
	
	// We can have our FHIR message saved as a QuickStream or a Dynamic Object inside the json property of the request
	If pRequest.QuickStreamId'="" {
		
		// Recover QuickStream of the FHIR message
		Set tQuickStream = ##class(HS.SDA3.QuickStream).%OpenId(pRequest.QuickStreamId)
		
		// Checking if message is in JSON format or XML
		If pRequest.Request.RequestFormatCode'="" {
			Set tFHIRFormat = pRequest.Request.RequestFormatCode
		} Else {
			$$$ThrowOnError(##class(HS.HC.Util).GetFormatFromData(tQuickStream, .tFHIRFormat))
			Do tQuickStream.Rewind()
			If tFHIRFormat="json" {
				Set tFHIRFormat = $$$FHIRContentCodeJSON
			} ElseIf tFHIRFormat="xml" {
				Set tFHIRFormat = $$$FHIRContentCodeXML
			}
		}
		
		// Transform QuickStream to DynamicObject
		Set tDynObj = ..GetDynObj(tQuickStream, tFHIRMetadataSetKey, tFHIRFormat)
		
		
	} ElseIf (($IsObject(pRequest.Request.Json))&&(pRequest.Request.Json.%GetIterator().%GetNext())) {
		// Could have Json %DynamicObject if this host is called InProc.
		Set tDynObj = ..GetDynObj(pRequest.Request.Json, tFHIRMetadataSetKey)
		
	} Else {
		$$$ThrowStatus($$$ERROR($$$GeneralError, "FHIR interop request message missing FHIR content"))
	}
	
	// Transform Dynamic Object to string
	set tRequest = tDynObj.%ToJSON()
	
	// Send message to EMPI production
	Set tSC= ..Adapter.SendMessageString(tRequest,.tResponse)
	
	$$$ThrowOnError(pRequest.NewResponse(.pResponse))
	//Set pResponse.Response = ##class(HS.FHIRServer.API.Data.Response).%New()
	Set pResponse.Response.ResponseFormatCode = pRequest.Request.ResponseFormatCode
	
	set pResponse.Response.Status = 200
	set pResponse.ContentType = "text/plain"
	Quit tSC
}

ClassMethod GetDynObj(stream As %Stream.Object, fhirVersion As %String, fhirFormat As %String) As %DynamicObject
{
	
	set schema = ##class(HS.FHIRServer.Schema).LoadSchema(fhirVersion)
	if fhirFormat = $$$FHIRContentCodeJSON {
		set dynObj = {}.%FromJSON(stream)
	} elseif fhirFormat = $$$FHIRContentCodeXML {
		set dynObj = ##class(HS.FHIRServer.Util.XMLToJSON).XMLToJSON(stream, schema)
	}
	Quit dynObj
}

XData MessageMap
{
<MapItems>
  <MapItem MessageType="HS.FHIRServer.Interop.Request">
    <Method>SendFHIRRequest</Method>
  </MapItem>
</MapItems>
}

}

Analicemos paso a paso nuestra clase:

  1. El primer punto será conocer la versión de nuestro mensaje FHIR que habremos configurado en las opciones de nuestro Business Operation.
    Set tFHIRMetadataSetKey = $ZStrip($Piece(..FHIRMetadataSet, "/", 1), "<>W")
    Recuperamos el tipo de mensaje FHIR que estamos tratando, en nuestro caso será R4.
  2. A continuación extraemos el contenido del mensaje FHIR:
    // We can have our FHIR message saved as a QuickStream or a Dynamic Object inside the json property of the request
    	If pRequest.QuickStreamId'="" {
    		
    		// Recover QuickStream of the FHIR message
    		Set tQuickStream = ##class(HS.SDA3.QuickStream).%OpenId(pRequest.QuickStreamId)
    		
    		// Checking if message is in JSON format or XML
    		If pRequest.Request.RequestFormatCode'="" {
    			Set tFHIRFormat = pRequest.Request.RequestFormatCode
    		} Else {
    			$$$ThrowOnError(##class(HS.HC.Util).GetFormatFromData(tQuickStream, .tFHIRFormat))
    			Do tQuickStream.Rewind()
    			If tFHIRFormat="json" {
    				Set tFHIRFormat = $$$FHIRContentCodeJSON
    			} ElseIf tFHIRFormat="xml" {
    				Set tFHIRFormat = $$$FHIRContentCodeXML
    			}
    		}
    		
    		// Transform QuickStream to DynamicObject
    		Set tDynObj = ..GetDynObj(tQuickStream, tFHIRMetadataSetKey, tFHIRFormat)
    		
    		
    	} ElseIf (($IsObject(pRequest.Request.Json))&&(pRequest.Request.Json.%GetIterator().%GetNext())) {
    		// Could have Json %DynamicObject if this host is called InProc.
    		Set tDynObj = ..GetDynObj(pRequest.Request.Json, tFHIRMetadataSetKey)
    		
    	} Else {
    		$$$ThrowStatus($$$ERROR($$$GeneralError, "FHIR interop request message missing FHIR content"))
    	}
  3. Obtenemos el %DynamicObject de nuestro mensaje FHIR:
    ClassMethod GetDynObj(stream As %Stream.Object, fhirVersion As %String, fhirFormat As %String) As %DynamicObject
    {
    	
    	set schema = ##class(HS.FHIRServer.Schema).LoadSchema(fhirVersion)
    	if fhirFormat = $$$FHIRContentCodeJSON {
    		set dynObj = {}.%FromJSON(stream)
    	} elseif fhirFormat = $$$FHIRContentCodeXML {
    		set dynObj = ##class(HS.FHIRServer.Util.XMLToJSON).XMLToJSON(stream, schema)
    	}
    	Quit dynObj
    }
  4. Finalmente lo transformamos en un String y se lo enviamos vía TCP a nuestra producción del EMPI, devolviendo un 200 al sistema que nos envió el mensaje original:
    // Transform Dynamic Object to string
    	set tRequest = tDynObj.%ToJSON()
    	
    	// Send message to EMPI production
    	Set tSC= ..Adapter.SendMessageString(tRequest,.tResponse)
    	
    	$$$ThrowOnError(pRequest.NewResponse(.pResponse))
    	//Set pResponse.Response = ##class(HS.FHIRServer.API.Data.Response).%New()
    	Set pResponse.Response.ResponseFormatCode = pRequest.Request.ResponseFormatCode
    	
    	set pResponse.Response.Status = 200
    	set pResponse.ContentType = "text/plain"
    	Quit tSC

Pues ya tendríamos nuestro mensaje remitido a nuestra producción del EMPI. Observemos la traza de un mensaje de ejemplo:

En la siguiente entrega recuperaremos el String enviado, lo transformaremos nuevamente a un %DynamicObject y lo introduciremos en el EMPI para que ejecute la operación de registro/actualización.

¡Espero que os sea de utilidad!

Comentarios (0)1
Inicie sesión o regístrese para continuar