User bio
404 bio not found
Member since Jan 20, 2023
Posts:
Dean has not published any posts yet.
Replies:

I've written this, it still requires a bit of work for dealing with errors correctly as it does not yet use the reply action codes correctly.
 

ClassComponent.Oauth.HL7HttpOperationPassThroughExtendsEnsLib.HL7.Operation.HTTPOperation
{
///Content-Type header of the payload
PropertyContentTypeAs%String[InitialExpression="x-application/hl7-v2+er7; charset=utf-8",Required];
PropertyOAuthClientApplicationNameAs%String(MAXLEN=64,MINLEN=1)[InitialExpression="SystemApplicationName",Required];
PropertyOAuthScopeAs%String(MAXLEN=1024);
ParameterADAPTER="EnsLib.HTTP.OutboundAdapter";
PropertyAdapterAsEnsLib.HTTP.OutboundAdapter;
///Default is don't need all the info off the response so just return the Status line
PropertyCondensedHTMLResponseAs%Boolean[InitialExpression=1,Required];
///JSON formatted string of parameters to be sent with the OAuth2 request
///e.g. {"ContentType":"x-application/hl7-v2+er7; charset=utf-8","OAuthClientApplicationName":"SystemApplicationName","OAuthScope":"OAuth","OAuthTokenSessionId":"SYSTOKENID"}
PropertyParamsAs%String(MAXLEN=500000);
PropertyOAuthTokenSessionIdAs%String[InitialExpression="SYSTOKENID",Required];
ParameterSETTINGS="ContentType:Basic,OAuthClientApplicationName:OAuth,OAuthScope:OAuth,OAuthTokenSessionId:OAuth, Params:OAuth";
MethodSendMessage(pMsgOutAsEnsLib.HL7.Message,OutputpMsgInAsEnsLib.HL7.Message,pExpectedSequenceNumberAs%String)As%Status
{
    $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:SendMessage")
   
    Set pMsgIn=$$$NULLOREF, tHttpRequest=##class(%Net.HttpRequest).%New(), tHttpRequest.WriteRawMode=1
    // Add these two lines after the first in SendMessage. Change the content type to your desired header.
    Set tSC = tHttpRequest.SetHeader("Content-Type", ..ContentType)
    s tHttpRequest.EntityBody=##class(%IO.StringStream).%New()
    s tHttpResponse =##class(%Net.HttpResponse).%New()
    set sc=tHttpRequest.EntityBody.Write(pMsgOut.RawContent)
    s tHttpRequest.Port=443
    s tHttpRequest.FollowRedirect=1
   
    $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:SendMessage Rawcontent " _ tHttpRequest.EntityBody.Size _ " " _ tHttpRequest.EntityBody.Read(1000))
    Set tSC = ..AddAccessToken(.tHttpRequest)
    Set sessionId=..%SessionId
   
    $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:AddAccessToken " _ tSC _ " " _ sessionId)
     
    Quit:$$$ISERR(tSC) tSC
    // Copy the rest of the SendMessage Method from the standard EnsLib.HL7.Operation.HTTPOperation
   // Set tMetaIOStream=##class(%IO.MetaCharacterStream).%New(tHttpRequest.EntityBody)
    //Set tSC=..OutputFramedToIOStream(tMetaIOStream,pMsgOut,..Separators,pExpectedSequenceNumber,1,..IOLogEntry) Quit:$$$ISERR(tSC) tSC
   
    // Initialize the EntityBody as a StringStream
    Set tHttpRequest.EntityBody = ##class(%IO.StringStream).%New()
    // Convert HL7 message to a properly formatted string with segment separators
    Set hl7Content = "",seg=""
    $$$TRACE(pMsgOut.SegCountGet())
    For segmentIndex = 1:1:pMsgOut.SegCountGet() {
        Set segment = pMsgOut.GetSegmentAt(segmentIndex)
        Set hl7Content = hl7Content _ segment.OutputToString() _ " "// Append segment and carriage return
        s seg=""_segment.OutputToString()_" "
       
        Set tSC = tHttpRequest.EntityBody.Write(seg) Quit:$$$ISERR(tSC)
    }
    //$$$TRACE("Formatted HL7 Content: " _ hl7Content)
    // Write the HL7 content to the EntityBody
    //Set tSC = tHttpRequest.EntityBody.Write(hl7Content)
    //If $$$ISERR(tSC) Quit tSC
    //$$$TRACE(tHttpRequest.EntityBody.Read(10000))
    //Set tSC = tHttpRequest.EntityBody.Write(hl7Content)
   
    Set tHttpRequest.ResponseStream=##class(%IO.StringStream).%New()
    $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:Authorization " _ tHttpRequest.Authorization _ " " _ sessionId)
   
    Set tHttpResponse = ##class(%Net.HttpResponse).%New()
    S send="POST"
    $$$TRACE("Sending to "_..Adapter.URL)
    s tSC = ..SendRequest(.tHttpResponse,send,tHttpRequest, .pResponse)
    //$$$TRACE(tHttpResponse.Data)
   
    // If response body is empty, write status info into stream
    If tHttpResponse.Data.Size = 0 && tHttpResponse.StatusCode >0 {
        Do tHttpResponse.Data.Write(tHttpResponse.StatusCode _ " " _ tHttpResponse.StatusLine)
    }
    #; Account for Adapter generating an error based on StatusCode
    If $$$ISERR(tSC),'$$$StatusEquals(tSC,$$$EnsErrHTTPStatus) Quit tSC
    If $IsObject(tHttpResponse.Data),tHttpResponse.Data.Size {
        $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:Reponse " _ tHttpResponse.StatusCode _ " " _ sessionId)
        //check framing....
        $$$TRACE("RESPONSE FRAME")
        $$$TRACE(tHttpResponse.Data.Read())
        do tHttpResponse.Data.Rewind()
        $$$TRACE(tHttpResponse.Data.Read())
        do tHttpResponse.Data.Rewind()
        set tHL7=tHttpResponse.Data.Read()
        Set tSC1=..%Parser.ParseFramedIOStream(tHttpResponse.Data,.pMsgIn,0,..IOLogEntry) Quit:$$$ISERR(tSC1) $$$ADDSC(tSC,tSC1)
    }
    Set:$$$StatusEquals(tSC,$$$EnsErrHTTPStatus) tSC=$$$OK
    Quit:$$$ISERR(tSC) tSC
    #; If no body response message, construct an ACK message from the HTTP Status Code
    If '$IsObject(pMsgIn) {
        //$$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:Status " _ HttpResponse.StatusCode _ " " _ HttpResponse.StatusLine _ " " _ sessionId)
        Set pMsgIn=pMsgOut.NewReplyDocument()
        Set pMsgIn.Envelope="ACK_HTTP_"_+tHttpResponse.StatusCode_":"_tHttpResponse.StatusLine
        $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:Status " _ pMsgIn.Envelope)
        //Set tCode="A"_$Case(+tHttpResponse.StatusCode, 200:"A", 503:"R", :"E")
        //Set tCode = $Case(+tHttpResponse.StatusCode,  200:"AA", 204:"AA", 503:"AR", :"AE")
        // Full HTTP → HL7 MSA mapping
        Set tCode = $Case(+tHttpResponse.StatusCode,
            200:"AA", 201:"AA", 202:"AA", 204:"AA",
            400:"AE", 401:"AE", 403:"AE", 404:"AE",
            408:"AR", 429:"AR",
            500:"AR", 502:"AR", 503:"AR", 504:"AR",
            :"AE")
        #; Create a message object to represent the HTTP ACK ; set 00 control id, 2.1 version
        Do pMsgIn.SetValueAt($TR($P(pMsgIn.Envelope,":"),"_",pMsgIn.CS),"1:9")
        Set tControlId=pMsgOut.GetValueAt("1:10") Set tControlId=$S(""'=tControlId:tControlId,1:"00")
        Do pMsgIn.SetValueAt("00","1:10")
        Do pMsgIn.SetValueAt(2.1,"1:12")
        Set tDesc="HTTP "_$S("AA"=tCode:"",1:"(N)")_"ACK '"_tHttpResponse.StatusLine_"'"
        If $IsObject(tHttpResponse.Data) {
            Do tHttpResponse.Data.Rewind()
            Set tDesc=tDesc_$S('tHttpResponse.Data.Size:"",1:" : "_tHttpResponse.Data.Read(1000))
        }
        Set tMSAText="MSA"_pMsgIn.FS_tCode_pMsgIn.FS_tControlId_pMsgIn.FS_tDesc
        Set tAckMSA=##class(EnsLib.HL7.Segment).%New($LB(,1,,pMsgIn.Separators_tMSAText))
        Do pMsgIn.AppendSegment(tAckMSA)
    }
    Quit tSC
}
MethodAddAccessToken(ByRefpHttpRequestAs%Net.HttpRequest)As%Status
{
    $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:AddAccessToken")
    Set tSC = $$$OK
    #dim erroras%OAuth2.Error
    Try {
       
        ; sessionId has a 50 charactor limit
        ; This should support multiple requests in the same production session  
        Set sessionId=..OAuthTokenSessionId
        Set isAuthorised = ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..OAuthClientApplicationName,sessionId,..OAuthScope,.accessToken,,.responseProperties,.error)
        ;Throw:'$$$NULLOREF=error ..ExceptionFromOAuth2Error(error)
        Throw:$isobject(error) ..ExceptionFromOAuth2Error(error)
        If 'isAuthorised {
            $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:AddAccessToken isAuthorised false")
            //Add any Parameters
            s paramsarr = [].%FromJSON(..Params)
            s iterator = paramsarr.%GetIterator()
            s properties=""
            While iterator.%GetNext(.key,.value)
            {
                s properties(key)=value
            }
            Set tSC = ##class(%SYS.OAuth2.Authorization).GetAccessTokenClient(..OAuthClientApplicationName,..OAuthScope,.properties,.error,.sessionId)    
            $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:AddAccessToken isAuthorised " _ tSC)
            Throw:$isobject(error) ..ExceptionFromOAuth2Error(error)
        }
        ; sendType is the mode of sending access token to resource server
        ; The default is "header"
        Set sendType = "header"
        ;The default for sslConfiguration comes from the OAuth2.Client instance.        
        Set tSC  = ##class(%SYS.OAuth2.AccessToken).AddAccessToken(pHttpRequest,sendType,,..OAuthClientApplicationName,sessionId)
    } Catch exception {
        Set tSC = exception.AsStatus()
        $$$TRACE("EXCEPTION")
        $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:AddAccessToken Code " _ exception.Code)
        $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:AddAccessToken Location " _ exception.Location)
        $$$TRACE("Component.Oauth.HL7HttpOperationPassThrough:AddAccessToken Data " _ exception.Data)
    }
    Return tSC
}
MethodSendRequest(OutputtHttpResponseAs%Net.HttpResponse,sendAs%String,tHttpRequestAs%Net.HttpRequest,OutputpResponseAsEnsLib.HTTP.GenericMessage)As%Status
{
    Set tSC=$$$OK
    // Perform the send
    Set tSC = ..Adapter.SendFormDataArray(.tHttpResponse, send, tHttpRequest)
    $$$TRACE("done the send")
    // If adapter returned error AND there's HTTP data, wrap the data in a status
    If $$$ISERR(tSC) && $IsObject(tHttpResponse) && $IsObject(tHttpResponse.Data) && tHttpResponse.Data.Size {
        Set tSC = $$$ERROR($$$EnsErrGeneral, $$$StatusDisplayString(tSC)_":"_tHttpResponse.Data.Read())
        Return tSC   // <-- FIX
    }
    If $$$ISERR(tSC) {
        Return tSC   // <-- FIX
    }
    // Handle HTTP status codes
    If (tHttpResponse.StatusCode < 300) {
        Set tSC=$$$OK
    } Else {
        Set message=tHttpResponse.StatusCode_": "_tHttpResponse.StatusLine_". "
        If $IsObject(tHttpResponse.Data) {
            Try {
                While 'tHttpResponse.Data.AtEnd {
                    Set message = message _ tHttpResponse.Data.Read(,.tSC1)
                }
            } Catch ex {
                // ignore
            }
        }
    }
    // Always RETURN a value
    Return ..constructResponse(tHttpResponse,.pResponse)
}
MethodconstructResponse(pHttpResponseAs%Net.HttpResponse,pResponseAsEnsLib.HTTP.GenericMessage)As%Status[Internal]
{
    $$$TRACE("construct response")
    Set tSC=$$$OK
    if '..CondensedHTMLResponse{
    If $IsObject(pHttpResponse.Data) {
        Set tStream=pHttpResponse.Data
    } Else {
        Set tStream=##class(%Stream.GlobalBinary).%New()
        Set tSC=tStream.Write(pHttpResponse.Data)  Quit:$$$ISERR(tSC) tSC
    }
    Set pResponse=##class(EnsLib.HTTP.GenericMessage).%New(tStream,,pHttpResponse)
    }
    else{
    Set pResponse=##class(EnsLib.HTTP.GenericMessage).%New()
        }
    Do pResponse.HTTPHeaders.SetAt(pHttpResponse.StatusCode,"StatusCode")
    Do pResponse.HTTPHeaders.SetAt(pHttpResponse.StatusLine,"StatusLine")
    Quit tSC
}
MethodExceptionFromOAuth2Error(pErrorAs%OAuth2.Error)As%Exception.StatusException
{
    Set errorText = pError.AsString()
    $$$LOGERROR(errorText)
    Set tSC = $$$ERROR($$$GeneralError,errorText)
    Return ##class(%Exception.StatusException).CreateFromStatus(tSC)
    ;Or
    ;If you wish to create one with %New then the 4th argument is a $lb of data values to the error %Status, e.g.
    ;Set exception = ##class(%Exception.StatusException).%New(Name,Code,Location,$lb(arg1,arg2,arg3,arg4))
}
}
Dean White · Sep 27, 2024 go to post

Something like this operation should do it for you.
 

Class Component.Outbound.JsonHttpsOut Extends Ens.BusinessOperation
{

Parameter ADAPTER = "EnsLib.HTTP.OutboundAdapter";

Property Adapter As EnsLib.HTTP.OutboundAdapter;

Method SendMessage(pMsgOut As %DynamicObject, Output pMsgIn As %Net.HttpResponse) As %Status
{
    S tSC=$$$OK
    Set tHttpRequest=##class(%Net.HttpRequest).%New(), tHttpRequest.WriteRawMode=1, token =##class(%DynamicObject).%New(), tSC=$$$OK
    Set tHttpRequest.SSLConfiguration=..Adapter.SSLConfig
    Set tSC = tHttpRequest.SetHeader("Content-Type", ..ContentType)
    d tHttpRequest.EntityBody.Write(pMsgOut.%ToJSON())
    #dim tHttpResponse As %Net.HttpResponse
    Set tHttpResponse = ##class(%Net.HttpResponse).%New()
    S send="POST"
    Set tSC=..Adapter.SendFormDataArray(.tHttpResponse, send, tHttpRequest)
    if (tHttpResponse.StatusCode="200"){
        set message=tHttpResponse.StatusCode_": "_tHttpResponse.StatusLine_". "
        if $IsObject(tHttpResponse.Data) 
        {
               while 'tHttpResponse.Data.AtEnd {
                   set message=message_tHttpResponse.Data.Read(,.tSC1) 
                   if 'tSC1 quit
            }
            $$$TRACE(message)
            s tSC=$$$ERROR("9999",message)
        }
        s tSC=$$$OK
        }
    else
    {
        set message=tHttpResponse.StatusCode_": "_tHttpResponse.StatusLine_". "
        if $IsObject(tHttpResponse.Data) 
        {
               //set message="" 
               while 'tHttpResponse.Data.AtEnd {
                   set message=message_tHttpResponse.Data.Read(,.tSC1) 
                   if 'tSC1 quit
            }
            $$$TRACE(message)
            s tSC=$$$ERROR("9999",message)
        }
        Set ..Adapter.URL=$REPLACE(..Adapter.URL,..tmpurl,"")    //sets adapter url back to standard if it was last used to remove an MRN
    }

    Q tSC
}

Certifications & Credly badges:
Dean has no Certifications & Credly badges yet.
Followers:
Dean has no followers yet.
Following:
Dean has not followed anybody yet.