Yone Moreno · Dec 2, 2019 go to post

Hello Eduard, I have tried your answer, however it outputs:

ESBSSCC>set enMsg = "hello"
 
ESBSSCC>set esMsg = "hola"
 
ESBSSCC>set obj = {"language1":(enMsg),"language2":(esMsg)}
 
ESBSSCC>write obj.%ToJSON()
 
WRITE obj.%ToJSON()
^
<METHOD DOES NOT EXIST> *%ToJSON,%Library.Object
ESBSSCC>w $classname(obj)
%Library.Object

Being the version:

w $zversion
Cache for UNIX (Red Hat Enterprise Linux for x86-64) 2016.1.2 (Build 206U) Mon Jul 25 2016 16:47:58 EDT

And the documentation for the class %Library.Object, shows no method called %ToJSON()

Could you help me please?

Yone Moreno · Jan 11, 2020 go to post

Hello Jeffrey Drumm,

The OBX is defined as:

And I have used your suggestion:

However messages with OBX still going to the Operation without being filtered in the Process. They are only being filtered if we write COD or FIN inside the EVN.4, but it does not take into consideration if there are OBX, or not.

Yone Moreno · Feb 5, 2020 go to post

Hello Marc Mundt, you are right:

I have tried to comment the sentence: "do result.%Display()", so then the code would be:

However $$$LOGINFO("resultado actual: "_result.%CurrentResult) prints an empty result:

And as you said, do result.%Display(), was iterating over the result:


We now have the following code:

set statement=##class(%SQL.Statement).%New()
 
  set status = statement.%PrepareClassQuery("TablasBBDD.NotificacionesPUSH.RelacionAplicacionNotificacion","seleccionarTodo")
 
  set result = statement.%Execute()
  //do result.%Display()
 
  $$$LOGINFO("filas result: "_result.%ROWCOUNT)
 
  $$$LOGINFO("result: "_result)
 
  //$$$LOGINFO("clave: "_result.%Get("clave"))
 
  $$$LOGINFO("resultado actual: "_result.%CurrentResult)
 
  $$$LOGINFO("result number of columns: "_result.%ResultColumnCount)
 
  //$$$LOGINFO("result dumped: "_result.DumpResults())
 
  //$$$LOGINFO("resultado siguiente: "_result.%Next())
 
  set i = 0
  while (result.%Next() '= 0){
   set i=i+1
   $$$LOGINFO("i: "_i)
   $$$LOGINFO("Inside loop, current result: "_result.%CurrentResult)
   set printed = result.%CurrentResult.%Print()
   $$$LOGINFO("current result print: "_printed)
   //$$$LOGINFO("current result getData(0): "_result.%CurrentResult.%GetData(0))
   //$$$LOGINFO("current result column(0): "_result.%CurrentResult.Column(0))
  }

And when we check the trace, we see, there are 4 columns:

And we iterate once, so there is one row:

Being current result a SQL.ClassQueryResultSet:

However, why when we call:

set printed = result.%CurrentResult.%Print()

$$$LOGINFO("current result print: "_printed)

It shows:

How could we get, from result.%CurrentResult which is a %SQL.ClassQueryResultSet, the row's data? I mean, how could we print result's row's data?‽???

 

 

I have read:

- https://cedocs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.c…

Yone Moreno · Feb 13, 2020 go to post

 In addition we have tested sending an HL7 message from a test operation to the bussiness service, here is the example messag:

MSH|^~\&|7edit||7edit||20200210093324||QBP^Q21|MSG-20200210-093324-0270|P|2.5
QPD|MessageQueryName
RCP

Then, we see the following trace:


ERROR <Ens>ErrOutConnectionLost: Se ha perdido conexión HL7 Read con  - detectado mediante ERROR #5002: Error de cache: <READ>zReadLine+11^%IO.DeviceStream.1


So it looks like our service refuses to accept input HL7 messages.

In the Production we see it gets stucked:

And our service:

Shows no results:

Besides, we have checked our operation is referencing service's host and port:

 

How could we debug this bussiness service?

Yone Moreno · Feb 13, 2020 go to post

Hello Marc, I appreciate your help. Currently we have figured out how to fill the response message. However there is a challenge: the outer systems answers to us with the following structure:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header/>
    <SOAP-ENV:Body>
        <RSP_K21 xmlns="urn:hl7-org:v2xml">
            <MSH>
                <MSH.1>|</MSH.1>
                <MSH.2>^~\&amp;</MSH.2>
                <MSH.3>
                    <HD.1>anonymized</HD.1>
                </MSH.3>
                <MSH.4>
                    <HD.1>anonymized</HD.1>
                    <HD.2>anonymized</HD.2>
                </MSH.4>
                <MSH.5>
                    <HD.1>anonymized</HD.1>
                </MSH.5>
                <MSH.6>
                    <HD.1>anonymized</HD.1>
                </MSH.6>
                <MSH.7>
                    <TS.1>anonymized</TS.1>
                </MSH.7>
                <MSH.9>
                    <MSG.1>RSP</MSG.1>
                    <MSG.2>K22</MSG.2>
                    <MSG.3>RSP_K21</MSG.3>
                </MSH.9>
                <MSH.10>945a44fd-aba2-45fb-9753-d24e38d1fb14</MSH.10>
                <MSH.11>
                    <PT.1>anonymized</PT.1>
                </MSH.11>
                <MSH.12>
                    <VID.1>2.5</VID.1>
                </MSH.12>
                <MSH.13>1</MSH.13>
                <MSH.15>AL</MSH.15>
                <MSH.16>AL</MSH.16>
                <MSH.18>ASCII</MSH.18>
            </MSH>
            <MSA>
                <MSA.1>AA</MSA.1>
                <MSA.3>AA</MSA.3>
            </MSA>
            <QAK>
                <QAK.2>OK</QAK.2>
            </QAK>
            <QPD>
                <QPD.1>
                    <CE.1>Q22</CE.1>
                    <CE.3>Find Candidates</CE.3>
                </QPD.1>
                <QPD.3>
                    <QIP.1>@PID.3.1</QIP.1>
                    <QIP.2>35018</QIP.2>
                </QPD.3>
            </QPD>
            <RSP_K21.QUERY_RESPONSE>
                <PID>
                    <PID.1>1</PID.1>
                    <PID.2>
                        <CX.1>anonymized</CX.1>
                        <CX.2>anonymized</CX.2>
                    </PID.2>
                    <PID.3>
                        <CX.1>anonymized</CX.1>
                        <CX.4>
                            <HD.1>anonymized</HD.1>
                        </CX.4>
                        <CX.5>anonymized</CX.5>
                    </PID.3>
                    <PID.3>
                        <CX.1>anonymized</CX.1>
                        <CX.2>anonymized</CX.2>
                        <CX.4>
                            <HD.1>anonymized</HD.1>
                        </CX.4>
                        <CX.5>anonymized</CX.5>
                    </PID.3>
                    <PID.3>
                        <CX.1>anonymized</CX.1>
                        <CX.2>anonymized</CX.2>
                        <CX.4>
                            <HD.1>anonymized</HD.1>
                        </CX.4>
                        <CX.5>anonymized</CX.5>
                    </PID.3>
                    <PID.3>
                        <CX.1>anonymized</CX.1>
                        <CX.4>
                            <HD.1>anonymized</HD.1>
                        </CX.4>
                        <CX.5>anonymized</CX.5>
                    </PID.3>
                    <PID.3>
                        <CX.1>""</CX.1>
                        <CX.4>
                            <HD.1>anonymized</HD.1>
                        </CX.4>
                        <CX.5>anonymized</CX.5>
                    </PID.3>
                    <PID.3>
                        <CX.1>""</CX.1>
                        <CX.4>
                            <HD.1>anonymized</HD.1>
                        </CX.4>
                        <CX.5>anonymized</CX.5>
                    </PID.3>
                    <PID.4>
                        <CX.1>anonymized</CX.1>
                    </PID.4>
                    <PID.5>
                        <XPN.1>
                            <FN.1>anonymized</FN.1>
                        </XPN.1>
                        <XPN.2>anonymized</XPN.2>
                    </PID.5>
                    <PID.6>
                        <XPN.1>
                            <FN.1>anonymized</FN.1>
                        </XPN.1>
                    </PID.6>
                    <PID.7>
                        <TS.1>anonymized</TS.1>
                    </PID.7>
                    <PID.8>M</PID.8>
                    <PID.11>
                        <XAD.1>
                            <SAD.1>""</SAD.1>
                            <SAD.2>anonymized</SAD.2>
                            <SAD.3>""</SAD.3>
                        </XAD.1>
                        <XAD.2>""</XAD.2>
                        <XAD.3>anonymized</XAD.3>
                        <XAD.4>anonymized</XAD.4>
                        <XAD.5>anonymized</XAD.5>
                        <XAD.6>anonymized</XAD.6>
                        <XAD.9>""_""</XAD.9>
                        <XAD.10>""</XAD.10>
                        <XAD.11>""</XAD.11>
                    </PID.11>
                    <PID.13>
                        <XTN.1>anonymized</XTN.1>
                        <XTN.4>""</XTN.4>
                    </PID.13>
                    <PID.13>
                        <XTN.1>anonymized</XTN.1>
                    </PID.13>
                    <PID.14>
                        <XTN.1>anonymized</XTN.1>
                    </PID.14>
                    <PID.14>
                        <XTN.1>""</XTN.1>
                    </PID.14>
                    <PID.15>
                        <CE.1>""</CE.1>
                    </PID.15>
                    <PID.16>
                        <CE.1>""</CE.1>
                    </PID.16>
                    <PID.17>
                        <CE.1>anonymized</CE.1>
                        <CE.4>anonymized</CE.4>
                    </PID.17>
                    <PID.19>""</PID.19>
                    <PID.22>
                        <CE.1>anonymized</CE.1>
                    </PID.22>
                    <PID.23>anonymized</PID.23>
                    <PID.26>
                        <CE.1>anonymized</CE.1>
                    </PID.26>
                    <PID.27>
                        <CE.1>""</CE.1>
                    </PID.27>
                    <PID.28>
                        <CE.1>anonymized</CE.1>
                        <CE.2>anonymized</CE.2>
                        <CE.4>anonymized</CE.4>
                    </PID.28>
                    <PID.30>anonymized</PID.30>
                    <PID.31>anonymized</PID.31>
                    <PID.35>
                        <CE.1>anonymized</CE.1>
                        <CE.2>anonymized</CE.2>
                    </PID.35>
                    <PID.39>
                        <CWE.1>""</CWE.1>
                        <CWE.2>""</CWE.2>
                        <CWE.3>""</CWE.3>
                        <CWE.4>anonymized</CWE.4>
                        <CWE.5>""</CWE.5>
                    </PID.39>
                </PID>
                <PD1>
                    <PD1.3>
                        <XON.1>anonymized</XON.1>
                        <XON.2>anonymized</XON.2>
                        <XON.7>anonymized</XON.7>
                        <XON.8>
                            <HD.1>anonymized</HD.1>
                        </XON.8>
                        <XON.9>anonymized</XON.9>
                        <XON.10>""</XON.10>
                    </PD1.3>
                    <PD1.3>
                        <XON.9>""</XON.9>
                    </PD1.3>
                    <PD1.5>""</PD1.5>
                    <PD1.7>anonymized</PD1.7>
                    <PD1.8>TSI 002</PD1.8>
                    <PD1.9>anonymized</PD1.9>
                    <PD1.12>anonymized</PD1.12>
                </PD1>
            </RSP_K21.QUERY_RESPONSE>
        </RSP_K21>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The difficulty is that
RSP_K21.QUERY_RESPONSE
 has content inside the Operation:

But when it is passed to the process is empty:

[... rest of message... ]

Message:

Class Mensajes.Response.GestionPacientes.operacionResponse Extends Ens.Response [ ProcedureBlock ]
{

Property MSH As hl7.MSH.CONTENT;

Property SFT As list Of hl7.SFT.CONTENT;

Property MSA As hl7.MSA.CONTENT;

Property ERR As hl7.ERR.CONTENT;

Property QAK As hl7.QAK.CONTENT;

Property QPD As hl7.QPD.CONTENT;

Property RSPK21QUERYRESPONSE As list Of EsquemasDatos.Gasometros.hl7.RSPK21.QUERYRESPONSE.CONTENT(REFELEMENTQUALIFIED = 0, XMLNAME = "RSP_K21.QUERY_RESPONSE", XMLPROJECTION = "ELEMENT", XMLREF = 1);

Property DSC As hl7.DSC.CONTENT;

Data Structure:

Class EsquemasDatos.Gasometros.hl7.RSPK21.QUERYRESPONSE.CONTENT Extends (%RegisteredObject, %XML.Adaptor) [ ProcedureBlock ]
{

Parameter ELEMENTQUALIFIED = 0;

Parameter NAMESPACE = "urn:hl7-org:v2xml";

Parameter XMLNAME = "RSP_K21.QUERY_RESPONSE";

Parameter XMLSEQUENCE = 1;

Property PID As EsquemasDatos.GestionPacientes.hl71.PID.CONTENT(REFELEMENTQUALIFIED = 0, REFNAMESPACE = "urn:hl7-org:v2xml", XMLNAME = "PID", XMLREF = 1) [ Required ];

Property PD1 As EsquemasDatos.GestionPacientes.hl71.PD1.CONTENT(REFELEMENTQUALIFIED = 0, REFNAMESPACE = "urn:hl7-org:v2xml", XMLNAME = "PD1", XMLREF = 1);

Property NK1 As list Of EsquemasDatos.GestionPacientes.hl71.NK1.CONTENT(REFELEMENTQUALIFIED = 0, REFNAMESPACE = "urn:hl7-org:v2xml", XMLNAME = "NK1", XMLPROJECTION = "ELEMENT", XMLREF = 1);

Property QRI As EsquemasDatos.GestionPacientes.hl71.QRI.CONTENT(REFELEMENTQUALIFIED = 0, REFNAMESPACE = "urn:hl7-org:v2xml", XMLNAME = "QRI", XMLREF = 1);

}

Operation:

Class Operaciones.SOAP.Gasometros.GestionPacientes.SeleneHL7Service Extends Ens.BusinessOperation [ ProcedureBlock ]
{ Method operacion(pRequest As Mensajes.Request.GestionPacientes.operacionRequest, Output pResponse As Mensajes.Response.GestionPacientes.operacionResponse) As %Library.Status
{
 Set ..Adapter.WebServiceClientClass = "WSCLIENTE.GestionPacientes.ConsultaCandidatos"
 Set MSH=pRequest.MSH,SFT=pRequest.SFT,QPD=pRequest.QPD,DSC=pRequest.DSC
 Set tSC = ..Adapter.InvokeMethod("operacion",,.MSH,.SFT,.MSA,.ERR,.QAK,.QPD,pRequest.RCP,.RSPK21QUERYRESPONSE,.DSC) Quit:$$$ISERR(tSC) tSC
 set pResponse = ##class(Mensajes.Response.GestionPacientes.operacionResponse).%New()
 Set tSC = pRequest.NewResponse(.pResponse) Quit:$$$ISERR(tSC) tSC
 
 Set pResponse.MSH = $get(MSH)
 Set pResponse.SFT = $get(SFT)
 //do pResponse.SFT.Insert($get(SFT).GetAt(1))  Set pResponse.MSA = $get(MSA)
 Set pResponse.ERR = $get(ERR)
 Set pResponse.QAK = $get(QAK)
 Set pResponse.QPD = $get(QPD)
 Set pResponse.DSC = $get(DSC)
 //do pResponse.RSPK21QUERYRESPONSE.Insert($get(RSPK21QUERYRESPONSE).GetAt(1))
 Set pResponse.RSPK21QUERYRESPONSE = $get(RSPK21QUERYRESPONSE)
 
 
 $$$LOGINFO("RSPK21QUERYRESPONSE: "_RSPK21QUERYRESPONSE)
 $$$LOGINFO("RSPK21QUERYRESPONSE.PID1: "_RSPK21QUERYRESPONSE.GetAt(1))
 $$$LOGINFO("RSPK21QUERYRESPONSE.PID1: "_RSPK21QUERYRESPONSE.GetAt(1).PID.PID1.content)
 $$$LOGINFO("RSPK21QUERYRESPONSE.PID8: "_RSPK21QUERYRESPONSE.GetAt(1).PID.PID8.content)
 
 $$$LOGINFO("pResponse.RSPK21QUERYRESPONSE "_pResponse.RSPK21QUERYRESPONSE)
 $$$LOGINFO("pResponse.RSPK21QUERYRESPONSE.PID1: "_pResponse.RSPK21QUERYRESPONSE.GetAt(1))
 $$$LOGINFO("pResponse.RSPK21QUERYRESPONSE.PID1: "_pResponse.RSPK21QUERYRESPONSE.GetAt(1).PID.PID1.content)
 $$$LOGINFO("pResponse.RSPK21QUERYRESPONSE.PID8: "_pResponse.RSPK21QUERYRESPONSE.GetAt(1).PID.PID8.content)
     
 Quit $$$OK
}

Client:

Class WSCLIENTE.GestionPacientes.ConsultaCandidatos Extends %SOAP.WebClient [ ProcedureBlock ]
{

Method operacion(ByRef MSH As hl7.MSH.CONTENT(REFELEMENTQUALIFIED=0,REFNAMESPACE="urn:hl7-org:v2xml",XMLREF=1,REQUIRED=1), ByRef SFT As %ListOfObjects(ELEMENTTYPE="hl7.SFT.CONTENT",XMLPROJECTION="element",REFELEMENTQUALIFIED=0,REFNAMESPACE="urn:hl7-org:v2xml",XMLNAME="SFT",XMLREF=1), Output MSA As hl7.MSA.CONTENT(REFELEMENTQUALIFIED=0,REFNAMESPACE="urn:hl7-org:v2xml",XMLREF=1,REQUIRED=1), Output ERR As hl7.ERR.CONTENT(REFELEMENTQUALIFIED=0,REFNAMESPACE="urn:hl7-org:v2xml",XMLREF=1), Output QAK As hl7.QAK.CONTENT(REFELEMENTQUALIFIED=0,REFNAMESPACE="urn:hl7-org:v2xml",XMLREF=1,REQUIRED=1), ByRef QPD As hl7.QPD.CONTENT(REFELEMENTQUALIFIED=0,REFNAMESPACE="urn:hl7-org:v2xml",XMLREF=1,REQUIRED=1), RCP As hl7.RCP.CONTENT(REFELEMENTQUALIFIED=0,REFNAMESPACE="urn:hl7-org:v2xml",XMLREF=1,REQUIRED=1), Output RSPK21QUERYRESPONSE As %ListOfObjects(ELEMENTTYPE="EsquemasDatos.Gasometros.hl7.RSPK21.QUERYRESPONSE.CONTENT",XMLPROJECTION="element",REFELEMENTQUALIFIED=0,REFNAMESPACE="urn:hl7-org:v2xml",XMLNAME="RSP_K21.QUERY_RESPONSE",XMLREF=1), ByRef DSC As hl7.DSC.CONTENT(REFELEMENTQUALIFIED=0,REFNAMESPACE="urn:hl7-org:v2xml",XMLREF=1)) [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
 Do (..WebMethod("operacion","QBP_Q21")).Invoke($this,"anonymized",.MSH,.SFT,.MSA,.ERR,.QAK,.QPD,.RCP,.RSPK21QUERYRESPONSE,.DSC)
} }

Could be different our data strcture to the one being responded by the outer system? Why does the RSP_K21.QUERY_RESPONSE
 contents appear inside the Operation and they are not being shown inside the Process?

Yone Moreno · Feb 13, 2020 go to post

Hello Marc, we appreciate your help.

We have modify EsquemasDatos.Gasometros.hl7.RSPK21.QUERYRESPONSE.CONTENT to extend %SerialObject as:

Class EsquemasDatos.Gasometros.hl7.RSPK21.QUERYRESPONSE.CONTENT Extends (%SerialObject, %XML.Adaptor) [ ProcedureBlock ]
{

Parameter ELEMENTQUALIFIED = 0;

Parameter NAMESPACE = "urn:hl7-org:v2xml";

Parameter XMLNAME = "RSP_K21.QUERY_RESPONSE";

Parameter XMLSEQUENCE = 1;

Property PID As EsquemasDatos.GestionPacientes.hl71.PID.CONTENT(REFELEMENTQUALIFIED = 0, REFNAMESPACE = "urn:hl7-org:v2xml", XMLNAME = "PID", XMLREF = 1) [ Required ];

Property PD1 As EsquemasDatos.GestionPacientes.hl71.PD1.CONTENT(REFELEMENTQUALIFIED = 0, REFNAMESPACE = "urn:hl7-org:v2xml", XMLNAME = "PD1", XMLREF = 1);

Property NK1 As list Of EsquemasDatos.GestionPacientes.hl71.NK1.CONTENT(REFELEMENTQUALIFIED = 0, REFNAMESPACE = "urn:hl7-org:v2xml", XMLNAME = "NK1", XMLPROJECTION = "ELEMENT", XMLREF = 1);

Property QRI As EsquemasDatos.GestionPacientes.hl71.QRI.CONTENT(REFELEMENTQUALIFIED = 0, REFNAMESPACE = "urn:hl7-org:v2xml", XMLNAME = "QRI", XMLREF = 1);

And the Message Viewer stills showing an empty list:

How could we pass the Operation's list which is being retrieved from the Web Service Client, to the Process? We ask this because of it is quite curious to see it being replied from the Web Service to the Operation, where we can LOGINFO its propierties, and then, suddenly when we pass it to the Process, it is empty.

Yone Moreno · Feb 13, 2020 go to post

Marc you are right, we have test again and thanks to put Extends %SerialObject into
EsquemasDatos.Gasometros.hl7.RSPK21.QUERYRESPONSE.CONTENT , now it shows the list with its contents being returned to the process:

Yone Moreno · Feb 18, 2020 go to post

Yes Marc, you are right.

We have used the following code in the service:

set writer=##class(%XML.Writer).%New()
set status=writer.OutputToString()
If $$$ISERR(status)

Do $system.OBJ.DisplayError(status)
set status=writer.RootObject(pPesponse)
If $$$ISERR(status) Do $system.OBJ.DisplayError(status)

set response = writer.GetXMLString()

Quit response

Where we create a new empty XML writer, the we request the string, from the object's start, and finally we get it.

Yone Moreno · Mar 6, 2020 go to post

Hello, Rubens Silva. We have tried the settings and the message is being sent, however the accented letters still being replaced by strange characters:

We have tried:

set httpRequest.ContentCharset = "utf-8"
set httpRequest.ContentType = "application/json"
set httpRequest.ContentEncoding = "utf-8"
set httpRequest.NoDefaultContentCharset = 0

Yone Moreno · Mar 6, 2020 go to post

Hello, Marc Mundt. We have tried to use the following:

set httpRequest.ContentCharset = "UTF-8"
set httpRequest.ContentType = "application/json"
set httpRequest.ContentEncoding = "UTF-8"
set httpRequest.NoDefaultContentCharset = 0

And the message is still being stored with strange characters:

 
 

 

Yone Moreno · Mar 6, 2020 go to post

We observe:

 
linea: {"app_id":"5cf57b56-c3b4-4a0d-8938-4ac4466f93af","headings":{"en":"Cita Atención Primaria","es":"Cita Atención Primaria"},"subtitle":{"en":"C.P. ISORA","es":"C.P. ISORA"},"contents":{"en":"Aqui el contenido del mensaje si aplicase","es":"Aqui el contenido del mensaje si aplicase"},"data":{"centro":"C.P. ISORA","fecha":"yyy/mm/dd","hora":"hh:mm","profesional":"nombre del profesional","nomUsuario":"nombre de usuario","codcita":"idCita","sepuedeborrar":"1"},"include_player_ids":["114a63e3-1da9-40fa-a3ec-e6677466e11a"]}

 

Being the code:

Method CrearNotificacion(pRequest As Mensajes.Request.NotificacionesPUSH.CrearNotificacion, pResponse As Mensajes.Response.NotificacionesPUSH.CrearNotificacion) As %Library.Status
{
$$$LOGINFO("^data: "_^data)/*
Set body = {
     "app_id": (pRequest.idApp),
     "headings": {"en":(pRequest.notificacion.titulo),"es":(pRequest.notificacion.titulo)},
     "subtitle": {"en":(pRequest.notificacion.subtitulo),"es":(pRequest.notificacion.subtitulo)},
     "contents": {"en":(pRequest.notificacion.mensaje),"es":(pRequest.notificacion.mensaje)},
     "data":(^data),
     "include_player_ids": ["c2917a6f-6ecf-4f45-8b31-9b72538580fd"]
}
*/$$$LOGINFO("^idsDispositivos: "_^idsDispositivos)Set body = {
     "app_id"(pRequest.idApp),
     "headings"{"en":(pRequest.notificacion.titulo),"es":(pRequest.notificacion.titulo)},
     "subtitle"{"en":(pRequest.notificacion.subtitulo),"es":(pRequest.notificacion.subtitulo)},
     "contents"{"en":(pRequest.notificacion.mensaje),"es":(pRequest.notificacion.mensaje)},
     "data":(^data),
     "include_player_ids"(^idsDispositivos)
}//"headings": {"en":($ZCONVERT(pRequest.notificacion.titulo,"O","UTF8")),"es":(pRequest.notificacion.titulo)},set httpRequest = ##class(%Net.HttpRequest).%New()
set tResponse = ##class(%Net.HttpResponse).%New()
set httpRequest.Server = "onesignal.com"set httpRequest.ContentCharset = "UTF-8"
set httpRequest.ContentType = "application/json"
set httpRequest.ContentEncoding = "UTF-8"
set httpRequest.NoDefaultContentCharset = 0
//set httpRequest.ContentType = "application/json;charset=utf-8"
//do httpRequest.SetHeader("Content-Type","application/json; charset=utf-8")
//set httpRequest.ContentCharset = "utf-8"
//do httpRequest.SetHeader("Content-Encoding","gzip")//set httpRequest.Authorization = "Basic "_##class(Util.TablasMaestras).getValorMaestra("NOTIFICACIONESPUSH.PARAMETRIZACIONES","onesignal_apikey")
set httpRequest.Authorization = "Basic "_##class(Util.TablasMaestras).getValorMaestra("NOTIFICACIONESPUSH.PARAMETRIZACIONES","ONESIGNAL_APIKEY_SCS")
$$$LOGINFO("httpRequest.Authorization: "_httpRequest.Authorization)
set httpRequest.Https = 1set httpRequest.SSLConfiguration = "Certificado_SCS"
$$$LOGINFO("body.data: "_body.data)
set dataObject = ##class(%DynamicObject).%FromJSON(body.data)
$$$LOGINFO("dataObject: "_dataObject)
set body.data = dataObject$$$LOGINFO("body.'include_player_ids': "_body."include_player_ids")
set idsDispositivosObject = ##class(%DynamicObject).%FromJSON(body."include_player_ids")
$$$LOGINFO("idsDispositivosObject "_idsDispositivosObject)
set body."include_player_ids" = idsDispositivosObject//set status = ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(httpRequest.EntityBody, body,,,,"aeloqtuw")
Do httpRequest.EntityBody.Write(body.%ToJSON())
while(httpRequest.EntityBody.AtEnd=0){
set linea = httpRequest.EntityBody.Read()
}
$$$LOGINFO("linea: "_linea)set tSC = httpRequest.Post("https://onesignal.com/api/v1/notifications")set tResponse = httpRequest.HttpResponse
if $$$ISERR(tSC){
$$$ThrowOnError(tSC)
}
if (tResponse.Data.AtEnd = 0) {
//$$$LOGINFO("En AñadirDispositivo, tamaño de tResponse: "_tResponse.Data.Size)
set linea = tResponse.Data.Read()
//$$$LOGINFO("Linea: "_linea)
}set pResponse = ##class(Mensajes.Response.NotificacionesPUSH.CrearNotificacion).%New()
do pResponse.informacion.Write(linea)Quit pResponse
}
 

Yone Moreno · Mar 9, 2020 go to post

Hello,  Tomáš Vaverka,

We have tried to rewind httpRequest.EntityBody

And we see it is being stored in the API with the strange character.

The code is:

while(httpRequest.EntityBody.AtEnd=0){
 set linea = httpRequest.EntityBody.Read()
}

Do httpRequest.EntityBody.Rewind()

Being the complete code:

Method CrearNotificacion(pRequest As Mensajes.Request.NotificacionesPUSH.CrearNotificacion, pResponse As Mensajes.Response.NotificacionesPUSH.CrearNotificacion) As %Library.Status
{
$$$LOGINFO("^data: "_^data)$$$LOGINFO("^idsDispositivos: "_^idsDispositivos)Set body = {
     "app_id"(pRequest.idApp),
     "headings"{"en":(pRequest.notificacion.titulo),"es":(pRequest.notificacion.titulo)},
     "subtitle"{"en":(pRequest.notificacion.subtitulo),"es":(pRequest.notificacion.subtitulo)},
     "contents"{"en":(pRequest.notificacion.mensaje),"es":(pRequest.notificacion.mensaje)},
     "data":(^data),
     "include_player_ids"(^idsDispositivos)
}//"headings": {"en":($ZCONVERT(pRequest.notificacion.titulo,"O","UTF8")),"es":(pRequest.notificacion.titulo)},set httpRequest = ##class(%Net.HttpRequest).%New()
set tResponse = ##class(%Net.HttpResponse).%New()
set httpRequest.Server = "onesignal.com"set httpRequest.ContentCharset = "UTF-8"
set httpRequest.ContentType = "application/json"
set httpRequest.ContentEncoding = "UTF-8"
set httpRequest.NoDefaultContentCharset = 0
//set httpRequest.ContentType = "application/json;charset=utf-8"
//do httpRequest.SetHeader("Content-Type","application/json; charset=utf-8")
//set httpRequest.ContentCharset = "utf-8"
//do httpRequest.SetHeader("Content-Encoding","gzip")//set httpRequest.Authorization = "Basic "_##class(Util.TablasMaestras).getValorMaestra("NOTIFICACIONESPUSH.PARAMETRIZACIONES","onesignal_apikey")
set httpRequest.Authorization = "Basic "_##class(Util.TablasMaestras).getValorMaestra("NOTIFICACIONESPUSH.PARAMETRIZACIONES","ONESIGNAL_APIKEY_SCS")
$$$LOGINFO("httpRequest.Authorization: "_httpRequest.Authorization)
set httpRequest.Https = 1set httpRequest.SSLConfiguration = "Certificado_SCS"
$$$LOGINFO("body.data: "_body.data)
set dataObject = ##class(%DynamicObject).%FromJSON(body.data)
$$$LOGINFO("dataObject: "_dataObject)
set body.data = dataObject$$$LOGINFO("body.'include_player_ids': "_body."include_player_ids")
set idsDispositivosObject = ##class(%DynamicObject).%FromJSON(body."include_player_ids")
$$$LOGINFO("idsDispositivosObject "_idsDispositivosObject)
set body."include_player_ids" = idsDispositivosObject//set status = ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(httpRequest.EntityBody, body,,,,"aeloqtuw")
Do httpRequest.EntityBody.Write(body.%ToJSON())
while(httpRequest.EntityBody.AtEnd=0){
set linea = httpRequest.EntityBody.Read()
}Do httpRequest.EntityBody.Rewind()$$$LOGINFO("linea: "_linea)set tSC = httpRequest.Post("https://onesignal.com/api/v1/notifications")set tResponse = httpRequest.HttpResponse
if $$$ISERR(tSC){
$$$ThrowOnError(tSC)
}
if (tResponse.Data.AtEnd = 0) {
//$$$LOGINFO("En AñadirDispositivo, tamaño de tResponse: "_tResponse.Data.Size)
set linea = tResponse.Data.Read()
//$$$LOGINFO("Linea: "_linea)
}set pResponse = ##class(Mensajes.Response.NotificacionesPUSH.CrearNotificacion).%New()
do pResponse.informacion.Write(linea)
while(pResponse.informacion.AtEnd=0){
set informacion = pResponse.informacion.Read()
}
$$$LOGINFO("informacion: "_informacion)
Do pResponse.informacion.Rewind()Quit pResponse
}
 

In addition, we observe that in our Operation the notification is:


{"app_id":"5cf57b56-c3b4-4a0d-8938-4ac4466f93af","headings":{"en":"Cita Atención Primaria","es":"Cita Atención Primaria"},"subtitle":{"en":"C.P. ISORA","es":"C.P. ISORA"},"contents":{"en":"Aqui el contenido del mensaje si aplicase","es":"Aqui el contenido del mensaje si aplicase"},"data":{"centro":"C.P. ISORA","fecha":"yyy/mm/dd","hora":"hh:mm","profesional":"nombre del profesional","nomUsuario":"nombre de usuario","codcita":"idCita","sepuedeborrar":"1"},"include_player_ids":["114a63e3-1da9-40fa-a3ec-e6677466e11a"]}  

And it is being stored with rare characters:


How could we debug this behaviour?
 

Yone Moreno · Mar 10, 2020 go to post

Hello Tomáš Vaverka,

We have tried the following code:

set httpRequest.ContentType = "application/json"
  set httpRequest.ContentCharset = "UTF-8"
set httpRequest.ContentEncoding = "UTF-8"
set httpRequest.NoDefaultContentCharset = 0

And it does store the notification with the accented letters.

We appreciate that you have spent time and effort helping us in this task, Tomáš Vaverka.
 

Yone Moreno · Mar 29, 2020 go to post

Hello Eduard Lebedyuk, thanks for your answer.

We have tried to send two async calls, and then use a <sync> to wait for both responses. We thought the BPL execution flow would stop at <sync> until it gets all responses from the async calls. However we observe that both sync calls are sent and then the execution flow continues beyond <sync>.

To give more context, we are calling for specialists appointments, then for primary appointments, after that we have the sync, and finally some transformation and code as follows:

Being the trace, the following, where we see the two asyn call being sent:

However because of the execution flow continues after <sync> without having both responses, we have in our context empty responses, so then the transformation fails:

Besides we do get the responses, however they arrive after they are needed, because of primary appointments return after the Transform attempt was made, and the specialists appointments are retrieved after BPL sends a response to the service (which means we could not use the specialists appointments anymore).

The question is: how could we achieve to wait and stop BPL execution flow, until we do get both async calls' responses?

Yone Moreno · Jun 16, 2020 go to post

Helo Alexey, Eduard:

There will be dozens of files every day. We also think that we should do it through a linux shell script, which:

1º Detects the files modified (or created) on the last day, in a series of folders (which will be in the same directory)

2º Copy the files to the folder "internal to the server" (we need to copy them and not just move them, because the original file must be recorded because they are owned by a different system)

How would you approach the shell script? We appreciate your time

Yone Moreno · Jul 15, 2020 go to post

Inspired by Cristiano Silva's answer, we have found the following way to get the desired property:

..%Process.%CurrentResponseHeader.SourceConfigName
Yone Moreno · Aug 12, 2020 go to post

Hello Eduard

We would need to be able to send a REST POST meesage with the following body:

{
"provider":"provider",
"group""group",
"location""location",
"data""data",
"checkin""checkin",
"client""client",
"admin""admin",
"apiUrl""apiUrl"
}
 
 

And get the previous body inside our Service.

We have written as a Service:

Class Servicios.REST.HistoriaClinica.Videoconsulta Extends Ens.BusinessService
{ Parameter ADAPTER = "EnsLib.HTTP.InboundAdapter"; Method OnProcessInput(pInput As %RegisteredObject, Output pOutput As %RegisteredObject) As %Status
{
do ..enviarCitaCrearSala(pInput,.pOutput)
quit $$$OK
} Method enviarCitaCrearSala(pInput As %Stream.Object, Output pOutput As %Stream.Object) As %Status
{
//TODO: Get body


set tSC = ..SendRequestSync("Videoconsulta",body,.objetoSalida)
set claseAux = ##class(%ZEN.Auxiliary.jsonProvider).%New()
set tSC = claseAux.%WriteJSONStreamFromObject(.pOutput,.objetoSalida,,,,"aeloqtuw")

Quit tSC
}
Yone Moreno · Aug 14, 2020 go to post

Hello Kevin,

We are using the following Java version:

java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

If we execute the command with our jar (which includes the Java program and its dependencies):

jar -tf "AppSeguimientoAnadidaReferenciaJARSexternos.jar"

We see:

META-INF/MANIFEST.MF
org/
org/eclipse/
org/eclipse/jdt/
org/eclipse/jdt/internal/
org/eclipse/jdt/internal/jarinjarloader/
org/eclipse/jdt/internal/jarinjarloader/JIJConstants.class
org/eclipse/jdt/internal/jarinjarloader/JarRsrcLoader$ManifestInfo.class
org/eclipse/jdt/internal/jarinjarloader/JarRsrcLoader.class
org/eclipse/jdt/internal/jarinjarloader/RsrcURLConnection.class
org/eclipse/jdt/internal/jarinjarloader/RsrcURLStreamHandler.class
org/eclipse/jdt/internal/jarinjarloader/RsrcURLStreamHandlerFactory.class
radarCovidcrearToken/
radarCovidcrearToken/CCAATokenGeneratorTest.class
generated_private_base64.pem
aether-api-1.7.jar
aether-impl-1.7.jar
aether-spi-1.7.jar
aether-util-1.7.jar
asm-6.2.jar
bcpkix-jdk15on-1.58.jar
bcprov-jdk15on-1.58.jar
classworlds-1.1.jar
commons-cli-1.0.jar
commons-codec-1.14.jar
commons-io-2.5.jar
jackson-annotations-2.10.3.jar
jackson-core-2.10.3.jar
jackson-databind-2.10.3.jar
java-jwt-3.10.3.jar
junit-3.8.1.jar
plexus-build-api-0.0.4.jar
plexus-cipher-1.4.jar
plexus-classworlds-2.2.3.jar
plexus-compiler-api-2.8.4.jar
plexus-compiler-javac-2.8.4.jar
plexus-compiler-manager-2.8.4.jar
plexus-component-annotations-1.7.1.jar
plexus-container-default-1.0-alpha-9-stable-1.jar
plexus-interactivity-api-1.0-alpha-4.jar
plexus-interpolation-1.13.jar
plexus-interpolation-1.14.jar
plexus-java-0.9.10.jar
plexus-sec-dispatcher-1.3.jar
plexus-utils-2.0.4.jar
plexus-utils-2.0.5.jar
qdox-2.0-M9.jar
sisu-guice-2.1.7-noaop.jar
sisu-inject-bean-1.4.2.jar
sisu-inject-plexus-1.4.2.jar
java-jwt-3.10.3_2.jar
jackson-databind-2.10.3_2.jar
jackson-annotations-2.10.3_2.jar
jackson-core-2.10.3_2.jar
commons-codec-1.14_2.jar
bcpkix-jdk15on-1.58_2.jar
bcprov-jdk15on-1.58_2.jar

If we execute the command to locate the jar where is the class which generates the NoClassDefFoundError :

jar -tf "bcprov-jdk15on-1.58.jar"

We get:

...

org/bouncycastle/util/io/pem/
org/bouncycastle/util/io/pem/PemGenerationException.class
org/bouncycastle/util/io/pem/PemHeader.class
org/bouncycastle/util/io/pem/PemObject.class
org/bouncycastle/util/io/pem/PemObjectGenerator.class
org/bouncycastle/util/io/pem/PemObjectParser.class
org/bouncycastle/util/io/pem/PemReader.class
org/bouncycastle/util/io/pem/PemWriter.class
org/bouncycastle/util/test/

...

As you would notice it outputs the name of the missing class:

org/bouncycastle/util/io/pem/PemReader.class

What could we do?

Yone Moreno · Aug 14, 2020 go to post

Hello Kevin,

Yes you were right we did not include "/opt/contenedor/Java/AppSeguimientoAnadidaReferenciaJARSexternos.jar"

We have just added it and the exception continues.

Yone Moreno · Jan 5, 2021 go to post

Hello,

Thanks Marc Mundt for your help,

1 We have tried with the following code which reads a String which represents an image in pRequest.imagen and we send it into a MIME:

Class Operaciones.REST.miSCS.miSCSSubirImagenJPGlocal Extends EnsLib.REST.Operation { Parameter INVOCATION = "Queue";

/// 📤 Subir la imagenSanitaria
Method SubirImagen(pRequest As Mensajes.Request.miSCS.SubirImagen, pResponse As Mensajes.Response.miSCS.SubirImagen) As %Library.Status
{
    
    Set httpRequest=##class(%Net.HttpRequest).%New()
    set tResponse = ##class(%Net.HttpResponse).%New()
    
    Set msg=##class(%Net.MIMEPart).%New()
     Set msg.Body=##class(%GlobalCharacterStream).%New()
    Do msg.Body.Write(pRequest.imagen)
    Set msg.ContentType="multipart/form-data"
    
    // create MIME writer; write root MIME message
    Set writer=##class(%Net.MIMEWriter).%New()

    // Prepare outputting to the HttpRequestStream
    Set status=writer.OutputToStream(httpRequest.EntityBody)
    if $$$ISERR(status) {do $SYSTEM.Status.DisplayError(status) Quit}

    // Now write down the content
    Set status=writer.WriteMIMEBody(msg)
    if $$$ISERR(status) {do $SYSTEM.Status.DisplayError(status) Quit}
    
    set url      = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","url")
    set path     = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","path")
    set servicio = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","servicio")
    set recurso  = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","subirImagen")
            
    set URL = "http://"_url_path_servicio_recurso
    $$$LOGINFO("URL: "_URL)

    set tFormVarNames = "expediente,imagen"
    set tData("expediente") = pRequest.expediente
    set tData("imagen") = pRequest.imagen

    set tSC = ..Adapter.SendFormDataArray(.tResponse,"POST",httpRequest,.tFormVarNames,.tData,URL)
    $$$LOGINFO("tSC: "_tSC)
    
    if $$$ISERR(tSC){
            $$$ThrowOnError(tSC)
    }
    if (tResponse.Data.AtEnd = 0) {
        $$$LOGINFO("En SubirImagen, tamaño de tResponse: "_tResponse.Data.Size)
        set linea = tResponse.Data.Read()
        $$$LOGINFO("Linea: "_linea)
    }
    
    set pResponse = ##class(Mensajes.Response.miSCS.SubirImagen).%New()
    do pResponse.return.Write(linea)

    Quit pResponse
}

However when we see it using Whireshark we observe that it is being sent as text/html

2 We have also used the following approach, to read a JPG directly and send it into the MIME:

Class Operaciones.REST.miSCS.miSCSSubirImagenJPGlocal Extends EnsLib.REST.Operation
{

Parameter INVOCATION = "Queue";

/// 📤 Subir la imagenSanitaria
Method SubirImagen(pRequest As Mensajes.Request.miSCS.SubirImagen, pResponse As Mensajes.Response.miSCS.SubirImagen) As %Library.Status
{
    
    //Crear Request y Response HTTP
    Set httpRequest=##class(%Net.HttpRequest).%New()
    set tResponse = ##class(%Net.HttpResponse).%New()
    
    //Crear MIME
    Set msg=##class(%Net.MIMEPart).%New()
     Set msg.Body=##class(%GlobalCharacterStream).%New()
     
    //Probamos a leer una imagen local y a enviarla
    Set stream=##class(%Stream.FileCharacter).%New()
      Set sc=stream.LinkToFile("C:\Users\ext-ymorjim\Pictures\miSCS.jpg")
      While 'stream.AtEnd {
          Set linea=stream.Read()
      }
      $$$LOGINFO("linea: "_linea)
      
      //Escribir la imagen en el mensaje MIME
     Do msg.Body.Write(linea)
    //Do msg.Body.Write(pRequest.imagen)
    Set msg.ContentType="multipart/form-data"
    
    // create MIME writer; write root MIME message
    Set writer=##class(%Net.MIMEWriter).%New()

    // Prepare outputting to the HttpRequestStream
    Set status=writer.OutputToStream(httpRequest.EntityBody)
    if $$$ISERR(status) {do $SYSTEM.Status.DisplayError(status) Quit}

    // Now write down the content
    Set status=writer.WriteMIMEBody(msg)
    if $$$ISERR(status) {do $SYSTEM.Status.DisplayError(status) Quit}
    

    set url      = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","url")
    set path     = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","path")
    set servicio = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","servicio")
    set recurso  = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","subirImagen")
            
    set URL = "http://"_url_path_servicio_recurso
    $$$LOGINFO("URL: "_URL)

    set tFormVarNames = "expediente,imagen"
    set tData("expediente") = pRequest.expediente
    set tData("imagen") = pRequest.imagen
    //set tData("imagen") = linea

    set tSC = ..Adapter.SendFormDataArray(.tResponse,"POST",httpRequest,.tFormVarNames,.tData,URL)
    $$$LOGINFO("tSC: "_tSC)
    
    if $$$ISERR(tSC){
            $$$ThrowOnError(tSC)
    }
    if (tResponse.Data.AtEnd = 0) {
        $$$LOGINFO("En SubirImagen, tamaño de tResponse: "_tResponse.Data.Size)
        set linea = tResponse.Data.Read()
        $$$LOGINFO("Linea: "_linea)
    }
    
    set pResponse = ##class(Mensajes.Response.miSCS.SubirImagen).%New()
    do pResponse.return.Write(linea)

    Quit pResponse
}

With the previous code we see that a text/html is being sent too:

In both cases, the external system gives us a HTTP code 400:

     ERROR <Ens>ErrException: <THROW>zSubirImagen+40^Operaciones.REST.miSCS.miSCS.1 *%Exception.StatusException ERROR <Ens>ErrHTTPStatus: Received non-OK status 400 from remote HTTP server: 'HTTP/1.1 400 ' -- logged as '-' number - @' Set sc=tSC Throw:('sc) ##class(%Exception.StatusException).ThrowIfInterrupt(sc)'

How could we achieve to send it in the correct way? as it is being sent when we use POSTMAN:

How could we send the image as multipart/form-data?

> We have read:

https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.U…

https://docs.intersystems.com/irisforhealthlatest/csp/docbook/Doc.View…

How could we send the image as multipart/form-data as it is being sent by Postman?

➡️ Could you point us to same code examples, please?

Yone Moreno · Jan 5, 2021 go to post

Hello,

We have continued the development,

The output system now replies to us

However we do see that the 2nd MIME part, the image, is being sent from ENSEMBLE, different than the image sent with POSTMAN

1 Let's see what we send from Ensemble:

2 The following image show us what POSTMAN correctly sends:

If we examine and compare both images, we observe that the image sent with Postam is being recognized as a JPEG, however the one sent with Ensemble is not being sent as a JPEG

The code which we have written is:

Class Operaciones.REST.miSCS.miSCS Extends EnsLib.REST.Operation
{

Parameter INVOCATION = "Queue";

/// 📤 Subir la imagen 
Method SubirImagen(pRequest As Mensajes.Request.miSCS.SubirImagen, pResponse As Mensajes.Response.miSCS.SubirImagen) As %Library.Status
{
    
    //Crear Request y Response HTTP
    Set httpRequest=##class(%Net.HttpRequest).%New()
    set tResponse = ##class(%Net.HttpResponse).%New()
    
    // Create root MIMEPart, la parte que contiene al resto
    Set RootMIMEPart=##class(%Net.MIMEPart).%New()
    
    //Crear parte con el expediente
     Set ExpedienteMIMEPart=##class(%Net.MIMEPart).%New()
     Set contentdisp="form-data; name=""expediente"""
     Do ExpedienteMIMEPart.SetHeader("Content-Disposition",contentdisp)
     Set ExpedienteMIMEPart.Body=pRequest.expediente
     //Do ExpedienteMIMEPart.SetHeader("Content-Type","text/plain")
     
    // Crear parte con la imagen
    Set ImagenMIMEPart=##class(%Net.MIMEPart).%New()
    Set ImagenMIMEPart.Body=##class(%GlobalCharacterStream).%New()
    //Probamos a leer una imagen local
    Set stream=##class(%Stream.FileCharacter).%New()
      Set sc=stream.LinkToFile("C:\Users\ext-ymorjim\Pictures\miSCS.jpg")
      While 'stream.AtEnd {
          Set linea=stream.Read()
      }
      $$$LOGINFO("linea: "_linea)
      //Escribir la imagen en el mensaje MIME
     Do ImagenMIMEPart.Body.Write(linea)
    //Do ImagenMIMEPart.Body.Write(pRequest.imagen)

    // Cabeceras de la imagen
    Set ImagenMIMEPart.ContentType="image/jpeg"
    Set contentdisp="form-data; name=""imagen""; filename="""_stream.Filename_""
    Do ImagenMIMEPart.SetHeader("Content-Disposition",contentdisp)

    // Insertar las partes en la raiz
    Do RootMIMEPart.Parts.Insert(ExpedienteMIMEPart)
    Do RootMIMEPart.Parts.Insert(ImagenMIMEPart)
    
    // create MIME writer; write root MIME message
    Set writer=##class(%Net.MIMEWriter).%New()

    // Prepare outputting to the HttpRequestStream
    Set httpRequest=##class(%Net.HttpRequest).%New()
    Set status=writer.OutputToStream(httpRequest.EntityBody)
    if $$$ISERR(status) {do $SYSTEM.Status.DisplayError(status) Quit}

    // Now write down the content
    Set status=writer.WriteMIMEBody(RootMIMEPart)
    if $$$ISERR(status) {do $SYSTEM.Status.DisplayError(status) Quit}

    // Creamos la URL
    set url      = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","url")
    set path     = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","path")
    set servicio = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","servicio")
    set recurso  = ##class(Util.TablasMaestras).getValorMaestra("MISCS.PARAMETRIZACIONES","subirImagen")
            
    set URL = "http://"_url_path_servicio_recurso
    $$$LOGINFO("URL: "_URL)
    
    
    //Escribimos el tipo de contenido que enviamos
    Set httpRequest.ContentType="multipart/form-data; boundary="_RootMIMEPart.Boundary
    $$$LOGINFO("> httpRequest.ContentType: "_httpRequest.ContentType)
    
    //Enviamos
    set tSC=httpRequest.Post(URL,0)
    $$$LOGINFO("tSC: "_tSC)
    
    if $$$ISERR(tSC){
            $$$ThrowOnError(tSC)
    }
    if (tResponse.Data.AtEnd = 0) {
        $$$LOGINFO("En SubirImagen, tamaño de tResponse: "_tResponse.Data.Size)
        set linea = tResponse.Data.Read()
        $$$LOGINFO("Linea: "_linea)
    }
    
    set pResponse = ##class(Mensajes.Response.miSCS.SubirImagen).%New()
    set pResponse.resultado = 1
    set pResponse.informacion = linea

    Quit pResponse
}

How could we continue
 

Could you point us to some example??, please

We have read:

https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.U…

Thanks in advance

Yone Moreno · Jan 29, 2021 go to post

Thanks Marc Mundt for your help,

We have tried to make objetoSalida.binario as %Binary

Exactly we do the following:

1) We read the image as binary directly from the external system in the REST Operation:

set linea=""
    if (tResponse.Data.AtEnd = 0) {
        set linea = tResponse.Data.Read()
    }
    
    set pResponse.binario = linea
    
    $$$LOGINFO("pResponse.binario: "_pResponse.binario)

We observe the binary correctly written in the LOGINFO:

2) In the Service, we have a LOGINFO  before writing it to JSON

 $$$LOGINFO("objetoSalida.binario: "_objetoSalida.binario)        

 set tSC = claseAux.%WriteJSONStreamFromObject(.pOutput,.objetoSalida,,,,"aeloqtuw")

We see it correctly:

3) However when we convert it to JSON we see that there are strange characters like "\x00"

set tSC = claseAux.%WriteJSONStreamFromObject(.pOutput,.objetoSalida,,,,"aeloqtuw")

$$$LOGINFO("pOutput.Read(): "_pOutput.Read())

How would you recommend us to continue?

What documents or code examples would you study or write to handle this issue?

Thanks for your help

Yone Moreno · Mar 8, 2021 go to post

Thanks Marc Mundt for your reply

Yes you are right, we should use POST

We have changed it:

<Route Url="/consultarImagen" Method="POST" Call="consultarImagen"/>

However we do not see the response in POSTMAN:

And the headers are:

Content-Type: text/html

Content-Length: 0

CACHE-CONTROL: no-cache

PRAGAM: no-cache

We send the POST to the following URL:

http://localhost:19622/aplicaciones/scs/test/miscs/consultarImagen
 

Besides we observe the response message being converted from Ensemble object to JSON in the service:

We do see the Response Message from the Operation to the Service

Why we do not see the JSON being replied from the Service in POSTMAN?

How could we debug this behaviour?

Thanks for your replies

Yone Moreno · Mar 8, 2021 go to post

Thanks Marc Mundt for your attention, and your helpful reply

 

Our current code is:

Class Servicios.REST.miSCS.Pruebas Extends EnsLib.REST.Service
{

Parameter ADAPTER = "EnsLib.HTTP.InboundAdapter";

Parameter EnsServicePrefix = "/aplicaciones/scs/test/miscs";

XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>

<Route Url="/:personType/:keyfield/:keyval/:getfield" Method="GET" Call="retrievePerson"/>
<Route Url="/consultarImagen" Method="POST" Call="consultarImagen"/>

</Routes>
}

/// Retrieve
Method retrievePerson(pInput As %Library.AbstractStream, Output pOutput As %Stream.Object, pPersonType As %String, pKeyField As %String, pKeyVal As %String, pGetField As %String = "") As %Status
{
    Set tType=$ZConvert(pPersonType,"L")  Quit:$Case(tType,"employee":0, "person":0, :1) $$$ERROR($$$EnsErrGeneral,"Directory type "_..#EnsServicePrefix_"/"_tType_"/ not supported.")
    Set $E(tType)=$ZConvert($E(tType),"U")
    Set tKeyIn=pKeyField, tKey=$ZConvert(tKeyIn,"L")  Quit:$Case(tKey,"name":0, "ssn":0, :1) $$$ERROR($$$EnsErrGeneral,"Directory key "_..#EnsServicePrefix_"/"_tType_"/"_tKey_" not supported.")
    Set tKeyVal=$Replace($ZConvert(pKeyVal,"I","URL"),"'","''")
    Set tField=pGetField  Set:""=tField tField="*"  Quit:tField["," $$$ERROR($$$EnsErrGeneral,"Commas not allowed in selection field; found: .../"_tField)
    Set tNS=$Namespace

    Set tKeyWild=$Translate(pKeyVal,"*?","%_")
    Do:tKeyWild'=pKeyVal pOutput.Write("[")
    ZNSpace "SAMPLES"
    try {
        Set tSel=$S("*"=tField:"ID",1:tField)
        Set tSQL="SELECT "_tSel_$S("*"=tField||(tKey=tSel):"", 1:","_tKey)_$Case("ID",tKey:"",tSel:"",:",ID")_" FROM Sample."_tType_" WHERE "_tKey_" LIKE '"_tKeyWild_"'"
        //$$$LOGINFO("tSQL: "_tSQL)
        Set tRS=##class(%ResultSet).%New()
        Set tSC=tRS.Prepare(tSQL)  Quit:$$$ISERR(tSC)
        Set tSC=tRS.Execute()  Quit:$$$ISERR(tSC)
        Set tFirst=1
        Set tOut=##class(%IO.StringStream).%New()
        While tRS.Next(.tSC) && $$$ISOK(tSC) {
            #; first normalize the case of the key and sel property names
            If tFirst {
                Set k="" For { Set k=$O(tRS.Data(k))  Quit:""=k
                    If $ZConvert(k,"L")=$Zconvert(tSel,"L") Set tSelN=k
                    If $ZConvert(k,"L")=$Zconvert(tKey,"L") Set tKeyN=k
                }
            }
            If $Case(tSelN, "Company":1, "Notes":1, "Home":1, "Office":1, :0) {
                Set tVal=tRS.Data("ID")
                Set tObj=$classmethod("Sample."_tType,"%OpenId",tVal,,.tSC)  Quit:$$$ISERR(tSC)
                Set tVal=$property(tObj,tSelN)
                Set tSelX = $Case(tSelN, "Home":"Addr", "Office":"Addr", :tSelN)
                Set tVal=$Case(tSelX, "Company":tVal.Name, "Notes":tVal.Read(), "Addr":tVal.Street_", "_tVal.City_" "_tVal.State_" "_tVal.Zip, :tVal)
            } Else {
                Set tVal=tRS.Data(tSelN)
            }
            If "*"=tField {
                Set tObj=$classmethod("Sample."_tType,"%OpenId",tVal,,.tSC)  Quit:$$$ISERR(tSC)
                Set tProxyObj=..buildProxyObj(tObj)
                Do tOut.Write($S(tFirst:"",1:","))
                Set tSC=..ObjectToJSONStream(tProxyObj,.tOut)
            } Else {
                Set:tKeyN'=tSelN tKeyFound=tRS.Data(tKeyN)
                Do tOut.Write($S(tFirst:"",1:",")_"{"_$S(tKeyN=tSelN:"",1:""""_tKeyIn_""":"""_tKeyFound_""", ")_""""_tSel_""":"""_tVal_"""}"_$C(13,10))
            }
            Set tFirst=0
            ZNSpace tNS
            Do tOut.Rewind()  Set tSC1=pOutput.Write(tOut.Read())  Do tOut.Clear()  Set:$$$ISOK(tSC) tSC=tSC1  Quit:$$$ISERR(tSC)
            ZNSpace "SAMPLES"
        } Quit:$$$ISERR(tSC)
        Do:tKeyWild'=tKeyVal pOutput.Write("]"_$C(13,10))
    } catch {
        Kill tRS
        ZNSpace tNS
        Set tSC=$$$SystemError
    }
    Kill tRS
    ZNSpace tNS
    $$$LOGINFO("tSQL: "_tSQL)
    Do:$$$ISOK(tSC) pOutput.SetAttribute("Content-Type","application/json")
    
    while (pOutput.AtEnd = 0){
        set respuestaFinal = pOutput.Read()
    }
    do pOutput.Rewind()
    $$$LOGINFO("respuestaFinal: "_respuestaFinal)
    
    Quit tSC
}

/// Normalize the Person or Employee info by copying its properties to a proxy object in a selective way
ClassMethod buildProxyObj(pObj As %Persistent) [ Internal ]
{
    Set tProxy = ##class(%ZEN.proxyObject).%New()
    Set tProxy.ID=pObj.%Id()
    Set tProxy.Name=pObj.Name
    Set tProxy.Age=pObj.Age
    Set tProxy.DOB=$ZDateTime(pObj.DOB,3)
    Set tProxy.SSN=pObj.SSN
    Set tProxy.FavoriteColors=pObj.FavoriteColors
    Set tProxy.Spouse=pObj.Spouse.Name
    Set tProxy.Home=..buildProxyAddr(pObj.Home)
    Set tProxy.Office=..buildProxyAddr(pObj.Office)
    If pObj.%IsA("Sample.Employee") {
        Set tProxy.Company=pObj.Company.Name
        Set tProxy.Notes=$S($IsObject(pObj.Notes):pObj.Notes.Read(),1:"")
    }
    Quit tProxy
}

ClassMethod buildProxyAddr(pObj As %SerialObject) [ Internal ]
{
    Set tProxy = ##class(%ZEN.proxyObject).%New()
    Set tProxy.Street=pObj.Street
    Set tProxy.City=pObj.City
    Set tProxy.State=pObj.State
    Set tProxy.Zip=pObj.Zip
    Quit tProxy
}

/// Control the type and content of error returned to the REST caller
ClassMethod OnErrorStream(pStatus As %Status)
{
     Set tStream = ##class(%GlobalBinaryStream).%New()  $$$ASSERT($IsObject(tStream))
    Do tStream.Write($ZConvert($$$StatusDisplayString(pStatus)_$C(13,10),"O","UTF8"))
    Set tStream.Attributes("Content-Type")=" text/plain; charset=""UTF-8"""
    Set tStream.Attributes("ResponseCode")="500 Internal Server Error"
     Quit tStream
}

/// Obtener la imagen guardada en TSI 📥📥📥📥
Method consultarImagen(pInput As %Library.AbstractStream, Output pOutput As %Stream.Object) As %Status
{
    Set pOutput=##class(%GlobalBinaryStream).%New()
    set claseAux = ##class(%ZEN.Auxiliary.jsonProvider).%New()
    
    //Convertir el body del JSON a objeto de Ensemble
    set body = pInput.Read()
    $$$LOGINFO("miSCS: body: "_body)
    do pInput.Rewind()
    
    //El mensaje esta en el body
    set tSC= claseAux.%ConvertJSONToObject(.body,"Mensajes.Request.miSCS.ConsultarImagen",.objetoEntrada,1)
    

    //Enviamos al Proceso
    set tSC = ..SendRequestSync("miSCS",objetoEntrada,.objetoSalida)

    //Convertimos el OBJETO devuelto por el Proceso en JSON
    //set tSC = claseAux.%WriteJSONStreamFromObject(.pOutput,.objetoSalida,,,,"aloqtuw")
    
    //Esta linea hace esta en el metodo de ejemplo "retrievePerson" y convierte el mensaje response en JSON
    Set tSC=..ObjectToJSONStream(objetoSalida,.pOutput)
    
    while (pOutput.AtEnd = 0){
        set respuesta = pOutput.Read()
    }
    do pOutput.Rewind()
    $$$LOGINFO("respuesta: "_respuesta)
    
    $$$LOGINFO("tSC: "_tSC)
    $$$LOGINFO("$$$ISOK(tSC): "_$$$ISOK(tSC))
    
    //Enviamos el JSON con cabeceras
    Do:$$$ISOK(tSC) pOutput.SetAttribute("Content-Type","application/json")
    do pOutput.SetAttribute("Access-Control-Allow-Origin","*")
    do pOutput.SetAttribute("Access-Control-Allow-Credentials","true")
    do pOutput.SetAttribute("Access-Control-Allow-Methods","GET")
    do pOutput.SetAttribute("Access-Control-Allow-Headers","request,Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")
    
    while (pOutput.AtEnd = 0){
        set respuestaFinal = pOutput.Read()
    }
    do pOutput.Rewind()
    $$$LOGINFO("respuestaFinal: "_respuestaFinal)
    Quit tSC
}

}

When we dig deeper using Whireshark:

First, we find that the method in the example that uses GET, the retrievePerson, does answers with a JSON to POSTMAN, so it works:

GET request:

GET response:

In POSTMAN:

However,

Our custom method: consultarImagen which is a POST

Shows nothing in POSTMAN

Why?

POST request:

POST response:

In POSTMAN:

Could you help us?

Thanks for your replies

Yone Moreno · Mar 8, 2021 go to post

Thanks Marc Mundt for your help

You are right

When we removed that line, the response is shown in POSTMAN:

Thanks four your help Marc

How did you know we should remove that line?

Yone Moreno · Mar 9, 2021 go to post

We are grateful Marc for your help,

Thanks for explaining how did you find the cause which was preventing to get the response in POSTMAN

Yone Moreno · Mar 9, 2021 go to post

Thanks Eduard for sharing this interesting piece of advice

Why would be better or recommended to use %CSP.REST directly, instead of EnsLib.REST.Service?

Are there any improvements if we use %CSP.REST?

Thanks for your reply

Yone Moreno · Apr 15, 2021 go to post

Hello Marc Mundt,

ITB.HL7.BS.XMLService code is the following:

/// HL7 XML services common class
Class ITB.HL7.BS.XMLService Extends (Ens.BusinessService, ITB.HL7.XMLHost)
{

/// Location and Revision of this file in Perforce (Auto-updating)
Parameter SrcVer = "$Id$";

Property UseAckCommitCodes As %Boolean [ InitialExpression = 1 ];

Property BadMessageHandler As %String(MAXLEN = 1000);

/// Name of the element to send the incoming XML stream received by this Service if message is processed OK
Property XMLInputHandler As %String(MAXLEN = 1000);

/// Colon-separated LocalFacility:LocalApplication:MessageStructure codes representing this (receiving) facility, application, returning MessageStructure, AcceptAcknowledgmentType and ApplicationAcknowledgmentType<br/>
/// These are used in constructing reply ACK message headers as SendingFacility, SendApplication and MessageStructure. <br/>
/// The '@' symbol represents using the corresponding field from the incoming message. <br/>
/// If your ID must contain a literal @ symbol, escape it with backslash: '\@'
Property LocalFacilityApplication As %String [ InitialExpression = "ISC:EnsembleHL7:ACK:NE:NE" ];

/// Strip namespace in HL7 XML (ACK message).
Property StripNamespace As %Boolean [ InitialExpression = 1 ];

/// Control of ACK handling; options: <br/>
/// - Never : Do not send back any ACK <br/>
/// - Immediate : Send back (commit) ACK reply message immediately upon receipt of the inbound message <br/>
/// - Application : If message passes validation, wait for ACK from target config item and forward it back when it arrives <br/>
Property AckMode As %String(DISPLAYLIST = ",Never,Immediate,Application", VALUELIST = ",Never,Immed,App") [ InitialExpression = "Immed", Required ];

/// Names the target(s) from which an ACK response should be forwarded back to the caller, if the AckMode="Application".
Property ResponseFrom As %String(MAXLEN = 1000);

Parameter SETTINGS = "StripNamespace,LocalFacilityApplication,AckMode,ResponseFrom::selector?context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},UseAckCommitCodes,TargetConfigNames:Basic:selector?multiSelect=1&context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},BadMessageHandler:Basic:selector?context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},XMLInputHandler:Basic:selector?context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId},SearchTableClass::selector?context={Ens.ContextSearch/SearchTableClasses?host=EnsLib.HL7.Service.Standard},MessageSchemaCategory:Basic:selector?context={Ens.ContextSearch/SchemaCategories?host=EnsLib.HL7.Service.Standard},AlertGracePeriod:Alerting";

/// HL7 XML (Stream) process input
Method StreamProcessInput(pInput As %Stream.Object, Output pOutput As %Stream.Object, pSendAck As %Boolean = 0, pCallTargets As %Boolean = 1, Output pER7 As EnsLib.HL7.Message) As %Status
{
    set ret = $$$OK
    
    try {
        // convert XML input to ER7
        set tER7 = ##class(ITB.HL7.Util.Convert).XMLToER7(pInput,.tSC,..MessageSchemaCategory)
        if $$$ISERR(tSC) $$$ThrowStatus(tSC)
        set pER7 = tER7
        
        // send ACK
        if pSendAck,..AckMode="Immed" {
            set tAckCode = $case(..UseAckCommitCodes, 1:"CA", 0:"AA")
            set tAckER7 = ..GetAck(tER7, tAckCode)
            set tAckXML = ##class(ITB.HL7.Util.Convert).ER7ToXML(tAckER7,.tSC,,,,..StripNamespace)
            if $$$ISERR(tSC) $$$ThrowStatus(tSC)
            set pOutput = tAckXML
        }
        
        // send EnsLib.HL7.Message to targets
        if pCallTargets {
            for i=1:1:$l(..TargetConfigNames, ",") {
                set tTarget=$zstrip($p(..TargetConfigNames,",",i),"<>W")
                if pSendAck,..AckMode="App",..ResponseFrom=tTarget {
                    $$$THROWONERROR(tSC,..SendRequestSync(tTarget, tER7, .tAckER7))
                    set tAckXML = ##class(ITB.HL7.Util.Convert).ER7ToXML(tAckER7,.tSC,,,,..StripNamespace)
                    if $$$ISERR(tSC) $$$ThrowStatus(tSC)
                    set pOutput = tAckXML
                } else {
                    $$$THROWONERROR(tSC,..SendRequestAsync(tTarget, tER7))
                }
            }
        }
        
        // index HL7 in SearchTable
        if ..SearchTableClass'="" {
            set tSC = $zobjclassmethod(..SearchTableClass,"IndexDoc",tER7)
            if $$$ISERR(tSC) $$$LOGERROR("SearchTableClass Error: "_##class(%SYSTEM.Status).GetErrorText(tSC))
        }
        
        // ok. send XML input to XMLInputHandler if any
        do:..XMLInputHandler'="" ..SendStreamToTarget(..XMLInputHandler,pInput)
        
    } catch ex {
        set ret = ex.AsStatus()
        $$$LOGERROR($$$StatusDisplayString(ret))
        
        // error occured. send service input to BadMessageHandler if any
        do:..BadMessageHandler'="" ..SendStreamToTarget(..BadMessageHandler,pInput)
        
        // send alert when HL7 XML has not been processed correctly
        do:..AlertOnError ..SendAlert(##class(Ens.AlertRequest).%New($LB(..%ConfigName,$$$StatusDisplayString(ret))))
    }
    
    quit ret
}

/// Get ACK message for a given HL7 message
Method GetAck(pMsg As EnsLib.HL7.Message, pReplyCode As %String) As EnsLib.HL7.Message
{
    // create ACK and copy the control id to the ack control id
    set tReply = pMsg.NewReplyDocument(,..LocalFacilityApplication)
    set tReply.Source = pMsg.%Id()
    do tReply.SetValueAt(pMsg.GetValueAt("1:10"),"1:10")
    do tReply.SetValueAt($p(..LocalFacilityApplication,":",3),"1:9.3")
    do tReply.SetValueAt($p(..LocalFacilityApplication,":",4),"1:15")
    do tReply.SetValueAt($p(..LocalFacilityApplication,":",5),"1:16")
    
    // MSA segment
    set tMSA=##class(EnsLib.HL7.Segment).%New($LB("",1))
    set tMSA.Separators=tReply.Separators
    do tMSA.SetValueAt("MSA",0)
    do tMSA.SetValueAt(pReplyCode,1)
    do tMSA.SetValueAt(pMsg.GetValueAt("1:10"),2)
    do tReply.AppendSegment(tMSA)
    
    quit tReply
}

/// Send pInput stream to a production target
Method SendStreamToTarget(pTarget As %String, pInput As %Stream.Object) As %Status
{
    set tMsg = ##class(Ens.StreamContainer).%New(pInput)
    set tSC = ..SendRequestAsync(pTarget, tMsg)
    if $$$ISERR(tSC) $$$LOGERROR(##class(%SYSTEM.Status).GetOneErrorText((tSC)))
    quit tSC
}

/// Return an array of connections for drawing lines on the config diagram
ClassMethod OnGetConnections(Output pArray As %String, pItem As Ens.Config.Item)
{
    do ##super(.pArray,pItem)
    
    if pItem.GetModifiedSetting("TargetConfigNames",.tValue) {
        
        set:pItem.GetModifiedSetting("BadMessageHandler",.tBadMessageHandler) tValue=tValue_","_tBadMessageHandler
        set:pItem.GetModifiedSetting("XMLInputHandler",.tXMLInputHandler) tValue=tValue_","_tXMLInputHandler
        
        for i=1:1:$L(tValue,",") {
            set tOne=$zstrip($p(tValue,",",i),"<>W")
            continue:""=tOne
            set pArray(tOne)=""
        }
    }
}

}

GetAck is a "Method"

How should we call it using method syntax, Marc Mundt?

Thanks for your replies

Yone Moreno · May 28, 2021 go to post

Thanks Jeffrey

We have placed your function in a class called "Util.FuncionesComunes"

We have tried to execute it as follows:

Set hosts=##class(Util.FuncionesComunes).GetHostsByAdapter("Produccion.ESBSSCC","EnsLib.HTTP.InboundAdapter")

It shows:

SET hosts=##class(Util.FuncionesComunes).GetHostsByAdapter("Produccion.ESBSSCC",
^
"EnsLib.HTTP.InboundAdapter")
<COMMAND>^Util.FuncionesComunes.1

When we write: "w $ZERROR" it outputs:

SET hosts=##class(Util.FuncionesComunes).GetHostsByAdapter("Produccion.ESBSSCC",
^
"EnsLib.HTTP.InboundAdapter")
<COMMAND>^Util.FuncionesComunes.1

How could we solve this?