Yone Moreno · May 31, 2021 go to post

Thanks Jeffrey for your kind help, and thanks for your time.

We have wrote your method in a class called "Util.FuncionesComunes"

Namespace "ESBSSCC"

We have wrote in the terminal:

ESBSSCC 2e1>Set hosts=##class(Util.FuncionesComunes).GetHostsByAdapter("Producion.ESBSSCC","EnsLib.HTTP.InboundAdapter")
 

We observe:


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

ESBSSCC 2e1>

Production's name is: Producion.ESBSSCC

How could we debug this to be able to execute the code and list all REST services in the current namespace?

Thanks for your help, time and replies Jeffrey

Yone Moreno · Jun 30, 2021 go to post

Hello, thanks for your time reading our questions and doubts,

We have studied how to use the following data types: %Stream.TmpCharacter and %Stream.TmpBinary

We have used both as follows:

              

               set pOutput = ##class(%Stream.TmpBinary).%New()
                //set pOutput = msg
                while(msg.AtEnd=0){
                    do pOutput.Write(msg.Read())
                }
                do msg.Clear()
                Quit $$$OK
 
                do pOutput.Clear()
                $$$LOGWARNING("Despues de borrar en el Servicio: pOutput.Clear()")

However we still observing a .stream being created at: /opt/ensemble/ESBHCDSNS/stream

How could we delete a .stream which represents a pdf being returned from a REST Service to POSTMAN?

Thanks for your help 🙇‍♂️  and time reading our questions and code ⌚️

➡️ We would need some help if you could explain to us how would you recommend to delete a .stream in a REST Service? , please.

We have also read:

https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cl…

https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic…

https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic…

https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls…

Yone Moreno · Jul 1, 2021 go to post

Hello Eduard thanks for your reply

msg is a %GlobalCharacterStream

it is defined before, here is the code:

                            set msg=##class(%GlobalCharacterStream).%New()
                            if ('tSC) || (response.error '= "")||(response.informacion.mensaje '= "") {
                                do msg.Write("<br></br><H1>No existe el informe solicitado</H1>")
                            } else {
                                do ..Adapter.AssignOneSetting("Pdf","1","")
                                Do msg.Write($SYSTEM.Encryption.Base64Decode(response.datos.pdf))
                            }
Yone Moreno · Aug 5, 2021 go to post

Thanks

Yone Moreno · Aug 9, 2021 go to post

Thanks Julius Kavay for your reply

Your contribution has helped us a lot, thanks, sincerely thanks.

Specially we thank you for your explanation it was very helpful!

Yone Moreno · Sep 24, 2021 go to post

Thanks Sean Connelly for your time and help answering to us

We have written:

Method cargarFichero(fichero As %GlobalBinaryStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
  //Header - Addresing
 set addressing = ..crearAddressing()
 
 set addressing.Action = "cargarFichero"
 
 set ..AddressingOut                = addressing
 set ..AddressingOut.mustUnderstand = "1"

 //Firma el XML (mensaje SOAP)
 //do ..crearSignature()
 
 set ..MTOMRequired=1
 
 //24 09 21 para añadir parametro name en cabecera content type
 set ..ContentType="application/octet-stream; name=nombre"
 
 Quit ..WebMethod("cargarFichero","CargarFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero",.fichero,.ccaaId,.tipoFichero)
}

However when we output the LOGSOAP we observe:

Output from Web client with SOAP action =http:// [endpoint]/cargarFichero
----boundary2247.8235294117647062276.235294117647059--
Content-Type: application/xop+xml; type="text/xml"; charset="UTF-8"
Content-Transfer-Encoding: 8bit
Content-Id: <0.E238359C.1D35.11EC.923C.005056B672A4>

    <?xml version="1.0" encoding="UTF-8" ?>
    <SOAP-ENV:Envelope xmlns:SOAP-ENV='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:s='http://www.w3.org/2001/XMLSchema' xmlns:wsa='http://www.w3.org/2005/08/addressing'>
        <SOAP-ENV:Header>
            <wsa:Action>cargarFichero</wsa:Action>
            <wsa:MessageID>e236513c1d3511ec923c005056b672a4</wsa:MessageID>
            <wsa:ReplyTo>
                <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
            </wsa:ReplyTo>
            <wsa:To>https://[endpoint]?wsdl</wsa:To>
        </SOAP-ENV:Header>
        <SOAP-ENV:Body>
            <CargarFicheroVacuRequest xmlns="http://[endpoint]">
                <fichero>
                    <xop:Include href="cid:1.E238359C.1D35.11EC.923C.005056B672A4" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
                </fichero>
                <ccaaId>01</ccaaId>
                <tipoFichero>2</tipoFichero>
            </CargarFicheroVacuRequest>
        </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
----boundary2247.8235294117647062276.235294117647059--
Content-Id: <1.E238359C.1D35.11EC.923C.005056B672A4>
Content-Transfer-Encoding: binary
CONTENT-TYPE: application/octet-stream

;;;47B7F6BF1C6D [... csv file content ...]

As you would observe in the LOGSOAP, the request is sending the headers as: "CONTENT-TYPE: application/octet-stream"

We would need to send:

Content-Type: application/octet-stream; name=1.E238359C.1D35.11EC.923C.005056B672A4

Content-Disposition: attachment; name="1.E238359C.1D35.11EC.923C.005056B672A4"; filename="1.E238359C.1D35.11EC.923C.005056B672A4"

Because it is being expected by the receiver system, to include the parameter "name" inside the header "Content-Type", as is shown in the next image:

How could we achieve this feature?

Would you recommend us some guide or documentation, to read about this topic?

Are there any examples that could help us?

Thanks in advance

Yone Moreno · Sep 27, 2021 go to post

Thanks David Hockenbroch for your help

We have written:

set ..HttpRequest.ContentType="application/octet-stream; name=nombre"
 do ..HttpRequest.SetHeader("name","nombre")

Being the full method as follows:

Method cargarFichero(fichero As %GlobalBinaryStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
  //Header - Addresing
 set addressing = ..crearAddressing()
 
 set addressing.Action = "cargarFichero"
 
 set ..AddressingOut                = addressing
 set ..AddressingOut.mustUnderstand = "1"

 //Firma el XML (mensaje SOAP)
 //do ..crearSignature()
 
 set ..MTOMRequired=1
 
 //24 09 21 para añadir parametro name en cabecera content type
 set ..ContentType="application/octet-stream; name=nombre"
 
 /*
     27 09 21 con el objetivo de poner parametro name en cabecera content type
 */
 set ..HttpRequest.ContentType="application/octet-stream; name=nombre"
 do ..HttpRequest.SetHeader("name","nombre")
 
 Quit ..WebMethod("cargarFichero","CargarFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero",.fichero,.ccaaId,.tipoFichero)
}

When we execute it, Ensemble throws an exception in the message viewer:

➡️ ERROR #5001: <INVALID OREF>zcargarFichero+16^WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP.1

We think it means that the variable "HttpRequest" is an invalid oref

How could we continue?

What steps would you recommend us to add the parameter "name" inside "Content-Type" header when we send a MTOM attachment using a SOAP request?

Thanks for your time, answers and help 💭

Yone Moreno · Oct 7, 2021 go to post

Thanks Jeffrey Drumm for your reply

We have observed that our Ensemble's version is:

Cache for UNIX (Red Hat Enterprise Linux for x86-64) 2017.2.1 (Build 801_3_18358U) Tue Jul 24 2018 16:36:10 EDT

It does not contain "RegexMatch" function in Ens.Util.FunctionSet

We have tried to develop a rule using Length and Contains functions

We would need to detect the following:

if at least one OBX3.1 is "24642" or at least one OBX3.5 = "24642"

What we have developed does not work, and we do not know why

Could you help us, please?

We have been testing, developing and debugging some hours, and we would really appreciate your time, replies, effort, and examples, please

The rule which does not work is:

Being the code:

<rule name="22 09 21 ADTs Antigenos DragoAE -&gt; DRAGOAP" disabled="true">
<constraint name="source" value="GestionPacientesFromSelenev02"></constraint>
<constraint name="msgClass" value="EnsLib.HL7.Message"></constraint>
<constraint name="docCategory" value="2.5"></constraint>
<when condition="(Document.{MSH:MessageType.TriggerEvent}=&quot;A08&quot;)&amp;&amp;(((Length(HL7.[OBX:3.1])=&quot;5&quot;)&amp;&amp;(HL7.[OBX:3.1] Contains &quot;24642&quot;))||((Length(HL7.[OBX:3.5])=&quot;5&quot;)&amp;&amp;(HL7.[OBX:3.5] Contains &quot;24642&quot;)))&amp;&amp;((Document.{OBX(1):ObservationResultStatus}=&quot;R&quot;)||(Document.{OBX(1):ObservationResultStatus}=&quot;C&quot;))">
<trace value="&quot;Se permiten ADTs con código de Antígenos en OBX3.5 o OBX3.1 y con OBX.11 = &apos;R&apos; Creaciones o &apos;C&apos; Modificaciones&quot;"></trace>
<send transform="" target="EnrutadorTestAntigenos"></send>
<return></return>
</when>
</rule>

Could you help us?

Would you know why it does not detect that the OBX3.1 and OBX3.5 are "24642"?

Thanks for your time

Yone Moreno · Oct 8, 2021 go to post

Thanks Jeffrey Drumm for your help, because the answer you have proposed works as expected

In addition you have helped us to learn about the square bracket syntax [] to find a value in a field in all repeating segments

Plus you have shared with us how does it outputs the values of the repeating segments: inside a string where each field's value is inside "<...>"

Thanks

Yone Moreno · Oct 15, 2021 go to post

We have been developing the Web Service following the official documentation:

Using MTOM for Attachments. Webclient

Specifically we have followed an example of a web service client where MTOMRequired property has been activated, and we have also changed the data types to %Stream.

We have written the following WebService:

Class WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP Extends %SOAP.WebClient [ ProcedureBlock ]
{

// Parameter LOCATION = "https://regvacube.sns.gob.es/regvacu/ws/FicheroVacuService";

/// This is the URL used to access the web service.
/// This is the namespace used by the Service
Parameter NAMESPACE = "http://ws.regvacuWs.ms.es/regvacu/ws/FicheroVacuService";

///  20/09/21 Cambiamos a 0, con el objetivo de quitar el xsi:type
Parameter OUTPUTTYPEATTRIBUTE = 0;

/// Determines handling of Security header.
Parameter SECURITYIN = "ALLOW";

/// This is the name of the Service
Parameter SERVICENAME = "FicheroVacuService";

// Parameter SOAPVERSION = 1.2;

Parameter SOAPVERSION = 1.1;

/// This is the SOAP version supported by the service.
Parameter MTOMREQUIRED = 1;

// Method cargarFichero(fichero As %xsd.base64Binary(REQUIRED=1), ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]

// Method cargarFichero(fichero As %GlobalBinaryStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]

/// 15 10 21 Edu explica que el fichero, el .zip con un .csv necesitamos enviarlo SIN CODIFICAR
/// quitamos %GlobalBinaryStream y ponemos %GlobalCharacterStream
///
Method cargarFichero(fichero As %GlobalCharacterStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
  //Header - Addresing
 set addressing = ..crearAddressing()
 
 set addressing.Action = "cargarFichero"
 
 set ..AddressingOut                = addressing
 set ..AddressingOut.mustUnderstand = "1"

 //Firma el XML (mensaje SOAP)
 //do ..crearSignature()
 
 set ..MTOMRequired=1
 
 //24 09 21 para añadir parametro name en cabecera content type
 //set ..ContentType="application/octet-stream; name=nombre"
 
 //28 09 21 probamos a ajustarlo
 //set ..ContentType="application/octet-stream; charset=latin1"
 set ..ContentType="application/xhtml+xml; charset=latin1"
 
 /*
     27 09 21 con el objetivo de poner parametro name en cabecera content type
     Genera excepcion:      ERROR #5001: <INVALID OREF>zcargarFichero+16^WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP.1
 */
 //set ..HttpRequest.ContentType="application/octet-stream; name=nombre"
 //do ..HttpRequest.SetHeader("name","nombre")
 
 Quit ..WebMethod("cargarFichero","CargarFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero",.fichero,.ccaaId,.tipoFichero)
}

Method infoFichero(ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), infoFichero As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(REQUIRED=1), Output estado As EsquemasDatos.HistoriaClinica.tns.EstadoFicheroType(REQUIRED=1)) As %xsd.base64Binary(XMLNAME="fichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
 set ..MTOMRequired=1    
 Quit ..WebMethod("infoFichero","InfoFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/infoFichero",.ccaaId,.infoFichero,.estado)
}

Method crearAddressing() As %SOAP.Addressing.Properties
{
    set IPRedSanitaria = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","IPRedSanitaria")
     set puertoRespuestas = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","PuertoRespuestasSSL")
     set ReplyTo = ##class(%SOAP.Addressing.EndpointReference).%New()
     set ReplyTo.Address = "http://www.w3.org/2005/08/addressing/anonymous"
     //set ReplyTo.Address = "https://"_IPRedSanitaria_":"_puertoRespuestas_"/csp/SNS/Servicios.ProgramasAsistenciales.SIFCOv02r00.cls"
     set MessageId = ##class(Util.FuncionesComunes).getUID()
     
     set addressing = ##class(%SOAP.Addressing.Properties).%New()
     set addressing.MessageId = MessageId
     set addressing.Destination = ..Location
     set addressing.ReplyEndpoint = ReplyTo    
          
     Quit addressing
}

Method crearSignature() As %XML.Security.Signature
{
       //Generamos el Binary Security Token a partir del mcertificado
     set x509alias = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","aliasCertMSSSI")
    set pwd = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","pwdCertMSSSI")
    set cred = ##class(%SYS.X509Credentials).GetByAlias(x509alias,pwd)
    set token = ##class(%SOAP.Security.BinarySecurityToken).CreateX509Token(cred)
    
    //Creamos la firma
    //set sig1=##class(%XML.Security.Signature).CreateX509(token,,$$$KeyInfoX509IssuerSerial)
    //set sig2=##class(%XML.Security.Signature).CreateX509(token,$$$SOAPWSIncludeSoapBody,$$$SOAPWSReferenceDirect)
    set sig2=##class(%XML.Security.Signature).CreateX509(token,$$$SOAPWSIncludeDefault,$$$SOAPWSReferenceDirect)
    
    //do sig1.SetSignatureMethod($$$SOAPWSrsasha1)
    do sig2.SetSignatureMethod($$$SOAPWSrsasha1)
    //do sig1.SetDigestMethod($$$SOAPWSsha1)
    do sig2.SetDigestMethod($$$SOAPWSsha1)
    
    //Creamos la referencia al id del token generado a partir de la firma
    //set algorithm=$$$SOAPWSEnvelopedSignature_","_$$$SOAPWSc14n
    set reference=##class(%XML.Security.Reference).Create(token.Id)
    do sig2.AddReference(reference)
    
    //Crear TimeStamp
    Set timestamp=##class(%SOAP.Security.Timestamp).Create()
    
    //Se une
    //do ..SecurityOut.AddElement(sig1)
    do ..SecurityOut.AddToken(token)
    do ..SecurityOut.AddElement(sig2)    
    Do ..SecurityOut.AddToken(timestamp)
}

}

The target system, asks us to implement a WebService with MTOM to send the zip with a csv inside, as follows:

When importing the WSDL of the target system, the header of the "cargarFichero" (loadFile) method was generated with "fichero" (file) as a "%xsd.base64Binary".

We changed file to "%GlobalCharacterStream".

This way the file, the csv, is sent unencoded, inside a CDATA

Being the response of the target system:

10/15/2021 08:57:24 *********************
Input to Web client with SOAP action = http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
--MIME_Boundary
Content-ID: <root.message@cxf.apache.org>
Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
Content-Transfer-Encoding: 8bit

        <?xml version="1.0" encoding="UTF-8"?>
        <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
            <soap:Body>
                <soap:Fault>
                    <faultcode>soap:Server</faultcode>
                    <faultstring>Name must not be null</faultstring>
                </soap:Fault>
            </soap:Body>
        </soap:Envelope>

--MIME_Boundary--

---------------
Validate Security header: action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero, MethodName=cargarFichero
**** SOAP client return error. method=cargarFichero, action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
     ERROR #6248: La respuesta de SOAP es un error de SOAP: faultcode=Server
faultstring=Name must not be null
faultactor=
detail=

However, as we can see in the first image, the target system would need, would require, that we send the file encoded in binary, since it says:

Content-Transfer-Encoding: binary

When we adapt the file to be "%GlobalBinaryStream" we see the following trace, where it is encoded in binary:

When sending binary encoded, the target system also responds:

09/28/2021 16:56:43 *********************
Input to Web client with SOAP action = http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
--MIME_Boundary
Content-ID: <root.message@cxf.apache.org>
Content-Type: application/xop+xml; type="text/xml"; charset=utf-8
Content-Transfer-Encoding: 8bit

            <?xml version="1.0" encoding="UTF-8"?>
            <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
                <soap:Body>
                    <soap:Fault>
                        <faultcode>soap:Server</faultcode>
                        <faultstring>Name must not be null</faultstring>
                    </soap:Fault>
                </soap:Body>
            </soap:Envelope>

--MIME_Boundary--

---------------
Validate Security header: action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero, MethodName=cargarFichero
**** SOAP client return error. method=cargarFichero, action=http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero
     ERROR #6248: La respuesta de SOAP es un error de SOAP: faultcode=Server
faultstring=Name must not be null
faultactor=
detail=

In short, the target system needs from us:

"add the "name" parameter inside the "Content-Type" header when attaching the file. Your client should generate this parameter to avoid getting this error."

Being the full comparison between what is sent by ensemble (without the "name" parameter, therefore incorrect) on the left; and what is generated by the SoapUI (with the name parameter, correct), on the right:

➡️ Please could you point us to examples, documentation, projects, code, that we can use as a reference to investigate, research and complete the development? 💭

We have also investigated the following answers:

https://community.intersystems.com/post/add-parameter-name-inside-conte…

Thank you very much for your time, reading and responding.

Thanks for your help.

Yone Moreno · Oct 15, 2021 go to post

We may need to add the Content-Disposition header in the request and there specify the file name.

In the example we had originally made with SoapUI, that header had this value:

Content-Disposition: attachment; name="application.zip"

Fine Tuning a WebClient tells you how you can specify headers in your web client using the SetHttpHeader() method that inherits from %SOAP.WebClient.

Following the explanation in the documentation, we have added:

do ..SetHttpHeader("Content-Disposition","application.zip")

We have written the line before:

Quit ..WebMethod("cargarFichero","CargarFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero",.fichero,.ccaaId,.tipoFichero)

When capturing the LOGSOAP we notice that it is left without including the Content-Disposition header and the name application.zip in what we send:

Why could it be that when writing the line, it is left without including the new Content-Disposition header and the application.zip file name? 💭🤔
 

Also, in other tests, we have written 3 additional ways, to try to solve it:

set ..ContentType="application/octet-stream; name=nombre"

Using the above line, we don't see that it adds the "name" parameter.

We tried the following line and it would generate an Ensemble exception:

set ..HttpRequest.ContentType="application/octet-stream; name=nombre"

ERROR #5001: <INVALID OREF>zcargarFichero+16^WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP.1

and using the next line:

do ..HttpRequest.SetHeader("name","nombre")

It gives us exception as well:

<INVALID OREF>zcargarFichero+16^WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP.1

The complete class we have written of the Web Service is:

Class WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP Extends %SOAP.WebClient [ ProcedureBlock ]
{

// Parameter LOCATION = "https://regvacube.sns.gob.es/regvacu/ws/FicheroVacuService";

/// This is the URL used to access the web service.
/// This is the namespace used by the Service
Parameter NAMESPACE = "http://ws.regvacuWs.ms.es/regvacu/ws/FicheroVacuService";

///  20/09/21 Cambiamos a 0, con el objetivo de quitar el xsi:type
Parameter OUTPUTTYPEATTRIBUTE = 0;

/// Determines handling of Security header.
Parameter SECURITYIN = "ALLOW";

/// This is the name of the Service
Parameter SERVICENAME = "FicheroVacuService";

// Parameter SOAPVERSION = 1.2;

Parameter SOAPVERSION = 1.1;

/// This is the SOAP version supported by the service.
Parameter MTOMREQUIRED = 1;

// Method cargarFichero(fichero As %xsd.base64Binary(REQUIRED=1), ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]

// Method cargarFichero(fichero As %GlobalBinaryStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]

/// 15 10 21 Edu explica que el fichero, el .zip con un .csv necesitamos enviarlo SIN CODIFICAR
/// quitamos %GlobalBinaryStream y ponemos %GlobalCharacterStream
///
Method cargarFichero(fichero As %GlobalCharacterStream, ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), tipoFichero As EsquemasDatos.HistoriaClinica.tns.TipoFicheroType(REQUIRED=1)) As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(XMLNAME="responseFichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
  //Header - Addresing
 set addressing = ..crearAddressing()
 
 set addressing.Action = "cargarFichero"
 
 set ..AddressingOut                = addressing
 set ..AddressingOut.mustUnderstand = "1"

 //Firma el XML (mensaje SOAP)
 //do ..crearSignature()
 
 set ..MTOMRequired=1
 
 //24 09 21 para añadir parametro name en cabecera content type
 //set ..ContentType="application/octet-stream; name=nombre"
 
 //28 09 21 probamos a ajustarlo
 //set ..ContentType="application/octet-stream; charset=latin1"
 //set ..ContentType="application/xhtml+xml; charset=latin1"
 
 /*
     27 09 21 con el objetivo de poner parametro name en cabecera content type
     Genera excepcion:      ERROR #5001: <INVALID OREF>zcargarFichero+16^WSCLIENTE.HistoriaClinica.FicheroVacuServiceSOAP.1
 */
 //set ..HttpRequest.ContentType="application/octet-stream; name=nombre"
 //do ..HttpRequest.SetHeader("name","nombre")
 
 
 /*
 15 10 21 seguimos la indicacion de Alberto Fuentes:
     ➕ añadir la cabecera Content-Disposition en la petición y ahí especificar el nombre del archivo.
     https://es.community.intersystems.com/post/%C2%BFc%C3%B3mo-podr%C3%ADamos-usar-mtom-para-enviar-un-zip-con-un-csv-adentro#comment-169536
     https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSOAP_cli_details#GSOAP_cli_details_http_headers
 */
 do ..SetHttpHeader("Content-Disposition","application.zip")
 
 Quit ..WebMethod("cargarFichero","CargarFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/cargarFichero",.fichero,.ccaaId,.tipoFichero)
}

Method infoFichero(ccaaId As EsquemasDatos.HistoriaClinica.tns.CCAAIdType(REQUIRED=1), infoFichero As EsquemasDatos.HistoriaClinica.tns.InfoFicheroType(REQUIRED=1), Output estado As EsquemasDatos.HistoriaClinica.tns.EstadoFicheroType(REQUIRED=1)) As %xsd.base64Binary(XMLNAME="fichero") [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, WebMethod ]
{
 set ..MTOMRequired=1    
 Quit ..WebMethod("infoFichero","InfoFicheroVacuRequest").Invoke($this,"http://ws.regvacuWs.ms.es/FicheroVacu/infoFichero",.ccaaId,.infoFichero,.estado)
}

Method crearAddressing() As %SOAP.Addressing.Properties
{
    set IPRedSanitaria = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","IPRedSanitaria")
     set puertoRespuestas = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","PuertoRespuestasSSL")
     set ReplyTo = ##class(%SOAP.Addressing.EndpointReference).%New()
     set ReplyTo.Address = "http://www.w3.org/2005/08/addressing/anonymous"
     //set ReplyTo.Address = "https://"_IPRedSanitaria_":"_puertoRespuestas_"/csp/SNS/Servicios.ProgramasAsistenciales.SIFCOv02r00.cls"
     set MessageId = ##class(Util.FuncionesComunes).getUID()
     
     set addressing = ##class(%SOAP.Addressing.Properties).%New()
     set addressing.MessageId = MessageId
     set addressing.Destination = ..Location
     set addressing.ReplyEndpoint = ReplyTo    
          
     Quit addressing
}

}

How could we understand, debug, tune, and resolve together, this situation?

If you could give us some guidance, we would appreciate it 🙇🙏🙏🙏🙏🙏

How would you recommend us to follow?

What documentation do we need to study, read, understand, to complete this part?

Thanks for your answers and time reading this question

Yone Moreno · Apr 1, 2022 go to post

Thanks Pasi Leino for your reply and help

We would be very grateful if you could help us with the following:

Could you explain us how could we download, install and start to use a free version od the Dicom Dvtk Ris-Emulator, please?

Yone Moreno · Sep 13, 2022 go to post

As a reference you could observe my following solution in Java:


 

import java.util.Arrays;

public classKata{
    public static int findShort(String s) {
        String[] words = s.split(" ");
        int shortestLength = 0;
        for(String word : words){
          if(shortestLength == 0){
            shortestLength = word.length();
          }else{
            if(shortestLength > word.length()){
              shortestLength = word.length();
            }
          }  
        }
        return shortestLength;
    }
}
Yone Moreno · Sep 14, 2022 go to post

Hello Dewey Hunt, thanks for your replies:

1) I am looking to develop current skills and/or generate new ones. But it would be prefered to deep into ObjectScript or into some skills related to interoperability.

2) It is interesting what you write about state of flow

Greetings

Yone Moreno · Sep 18, 2022 go to post

Example solutions:

dinglemouseChungGorrainrainEAnochlunacjulialebowAndrew P.Kuzmanov_MarioIvanKotsovskiZasho (+ 13)'s solutions

public class Kata {
  public static String declareWinner(Fighter fighter1, Fighter fighter2, String firstAttacker) {
    Fighter a=fighter1, b=fighter2;
    if (firstAttacker.equals(fighter2.name)) {
      a = fighter2; b = fighter1;
    }    
    while (true) {      
      if ((b.health -= a.damagePerAttack) <= 0) return a.name;  // a wins
      if ((a.health -= b.damagePerAttack) <= 0) return b.name;  // b wins
    }
  }
}

My own:

public class Kata {
  public static String declareWinner(Fighter fighter1, Fighter fighter2,
                String firstAttacker) {
    
    String nextAttacker = firstAttacker;
    do{
        if(nextAttacker.equals(fighter1.name)){  
          fighter2.health -= fighter1.damagePerAttack;
          if(fighter2.health > 0){
            nextAttacker = fighter2.name;
          }
        }else{
          fighter1.health -= fighter2.damagePerAttack;
          if(fighter1.health > 0){
            nextAttacker = fighter1.name;
          }
        }
    
    }while(fighter1.health > 0 && fighter2.health > 0);
    return nextAttacker;
  }
}

marko-bekhtaKutayBSVarakutarosedhivyaShreyasRanimeshp's solution
 

public class Kata {
  public static String declareWinner(Fighter fighter1, Fighter fighter2, String firstAttacker) {
        int moves1 = (int) Math.ceil( (double)fighter2.health / fighter1.damagePerAttack);
        int moves2 = (int) Math.ceil( (double)fighter1.health / fighter2.damagePerAttack);
        if (moves1 > moves2) {
            return fighter2.name;
        } else if (moves1 < moves2) {
            return fighter1.name;
        } else {
            return firstAttacker;
        }
  }
}

I hope you feel challenged with this exercise

Yone Moreno · Sep 19, 2022 go to post

As a reference a the Java solution:

Could be:

import java.util.*;

public class DnaStrand {

  static final Map<String,String> letters = Map.ofEntries(Map.entry("A","T"),Map.entry("T","A"),Map.entry("G","C"),Map.entry("C","G"));

  public static String makeComplement /*🧬🔁🧬*/ (String dna) {
    StringBuilder result = new StringBuilder();
    
    for(String letter:dna.split("")){
      result.append(letters.get(letter));
    }
    return result.toString();
  }
}

Would you like to improve this in ObjectScript?

Are you able?

Yone Moreno · Nov 28, 2022 go to post

🧐 Additionally we have read 💭💭 carefully:

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

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

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

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

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

https://community.intersystems.com/post/making-jwtoauth20

However, we do still have the same question:

What way is there to "call", "invoke", "communicate" from the other environments ( for example INTEGRATION ) with the PREPRODUCTION Resource Server in order to Validate the Token?

That is to say, the question in another way would be:

What mechanism exists to communicate from an Environment A (Integration) with an Environment B (Preproduction) with the mission to Validate the Token from Environment A using the centralized Resource Server available in Environment B?

👀 Thank you very much for your attention, and thank you for your answers.

Best regards

Yone Moreno · Apr 20, 2023 go to post

Hi Timothy,

Thank you so much for your response and for explaining the use of the $$$FormatText macro in ObjectScript. It's great to know that this macro simplifies the process of string formatting and can be particularly useful in conjunction with localization. 🙌

Best regards!

Yone Moreno · Apr 20, 2023 go to post

🙌 Thank you so much, Maria Nesterenko, for sharing the valuable testimony and explanation of how you developed the Sheep's Galaxy sleep analysis application using InterSystems IRIS Cloud SQL and IntegratedML technologies.

Your application is truly innovative and provides users with the tools to analyze and improve their sleep quality.

It's impressive how you took into account factors such as noise levels, room lighting, caffeine consumption, and more to help users create optimal conditions for sleep.

The use of Angular framework for frontend, FastApi framework with DB-API package for backend, and IRIS Cloud SQL with IntegratedML to analyze and store data is remarkable. Your team's work is inspiring, and we thank you for contributing to important issues in such a cutting-edge way. 👏

So, to summarize, does the application provide the user with a "sleep score" or does it have additional uses? 🤔

Yone Moreno · May 15, 2023 go to post

Thanks Alexander Koblov,

Why is $Listnext recommended?

Is it true?

The "$List" function returns a complete list, while "$ListGet" returns a specific item from a list given its index. When you need to iterate over a list, you can use the "$Listnext" function. This function allows you to get the elements of a list one by one, without having to load the whole list into memory at once.

Using "$Listnext" instead of "$List" or "$ListGet" can improve performance, as it avoids the need to load and process the entire list at once. Instead, each item can be fetched individually as needed, which is particularly useful in cases where the list is large.

Yone Moreno · May 15, 2023 go to post

Thanks for your reply Alex Woodhead. You seem to offer a very complete solution and handle some special cases, such as whitespace removal and case insensitive searches.

Yone Moreno · May 17, 2023 go to post

Thanks Robert Camper. I am grateful for your help, and I value the most your explanation rephrasing the issue with both examples, what I expect, what I get. Thanks for your didactic answer.

Yone Moreno · May 23, 2023 go to post

We would like to share our approach to try to solve this task:

First, we add the following lines inside the SOAP Service's method:

 #dim SAML AS %SAML.Assertion
 Set SAML = ..SecurityIn.FindElement("Assertion")  set writer=##class(%XML.Writer).%New()


 set status=writer.OutputToString()
 If $$$ISERR(status)  Do $system.OBJ.DisplayError(status)
 set status=writer.RootObject(SAML)
 If $$$ISERR(status) Do $system.OBJ.DisplayError(status)  set samlString = writer.GetXMLString()
 $$$LOGASSERT("samlString: "_samlString)  

set pRequest = ##class(Mensajes.Request.RESNS.RespondingGatewayCrossGatewayQueryRequest).%New()  set pRequest.CabeceraSAML = samlString
 

Being the current code as follows:

Class Servicios.RESNS.ConsultaRecetas Extends EnsLib.SOAP.Service [ ProcedureBlock ]
{

/// This is the namespace used by the ServiceParameter NAMESPACE = "urn:ihe:iti:xds-b:2007";/// Use xsi:type attribute for literal types.Parameter OUTPUTTYPEATTRIBUTE = 0;/// Determines handling of Security header.Parameter SECURITYIN = "IGNORE";/// This is the name of the ServiceParameter SERVICENAME = "RespondingGateway_Service";/// This is the SOAP version supported by the service.Parameter SOAPVERSION = 1.2;/// Namespaces of referenced classes will be used in the WSDL.Parameter USECLASSNAMESPACES = 1;Property sns As%String;Parameter SETTINGS = "sns";/// Default URL for invoking the WebService./// The URL may be absolute or relative to the WSDL request URL..
Method RespondingGatewayCrossGatewayQuery(RequestSlotList As EsquemasDatos.RESNS.v02r00.rim.SlotListType, id As%xsd.anyURI(XMLPROJECTION="attribute"), comment As%String(XMLPROJECTION="attribute"), ResponseOption As EsquemasDatos.RESNS.v02r00.query.ResponseOptionType(REFELEMENTQUALIFIED=1,REFNAMESPACE="urn:oasis:names:tc:ebxml-regrep:xsd:query:3.0",XMLREF=1), AdhocQuery As EsquemasDatos.RESNS.v02r00.rim.AdhocQueryType(REFELEMENTQUALIFIED=1,REFNAMESPACE="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0",XMLREF=1), federated As%Boolean(XMLPROJECTION="attribute"), federation As%xsd.anyURI(XMLPROJECTION="attribute"), ByRef startIndex As%Integer(XMLPROJECTION="attribute"), maxResults As%Integer(XMLPROJECTION="attribute"), Output ResponseSlotList As EsquemasDatos.RESNS.rim.SlotListType, Output RegistryErrorList As EsquemasDatos.RESNS.rs.RegistryErrorList(REFELEMENTQUALIFIED=1,REFNAMESPACE="urn:oasis:names:tc:ebxml-regrep:xsd:rs:3.0",XMLREF=1), Output status As EsquemasDatos.RESNS.rim.referenceURI(XMLPROJECTION="attribute"), Output requestId As%xsd.anyURI(XMLPROJECTION="attribute"), Output RegistryObjectList As EsquemasDatos.RESNS.rim.RegistryObjectListType(REFELEMENTQUALIFIED=1,REFNAMESPACE="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0",XMLREF=1), Output totalResultCount As%Integer(XMLPROJECTION="attribute")) [ Final, ProcedureBlock = 1, SoapAction = "urn:ihe:iti:2007:CrossGatewayQuery", SoapBindingStyle = document, SoapBodyUse = literal, SoapMessageName = AdhocQueryResponse, SoapTypeNameSpace = "urn:oasis:names:tc:ebxml-regrep:xsd:query:3.0", WebMethod ]
{
 #dim SAML AS%SAML.AssertionSet SAML = ..SecurityIn.FindElement("Assertion") 

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

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

 set samlString = writer.GetXMLString()
 $$$LOGASSERT("samlString: "_samlString)

 set pRequest = ##class(Mensajes.Request.RESNS.RespondingGatewayCrossGatewayQueryRequest).%New()

 set pRequest.CabeceraSAML = samlString

 set..MTOMRequired = 0set pRequest.RequestSlotList = RequestSlotList

[... other unrelated method code ...]

}
}


Second we have added the property in the Request:

Class Mensajes.Request.RESNS.RespondingGatewayCrossGatewayQueryRequest Extends Ens.Request [ ProcedureBlock ]
{

Parameter RESPONSECLASSNAME = "Mensajes.Response.RESNS.RespondingGatewayCrossGatewayQueryResponse";
[... other properties ...]

/// 22 05 2023 Añadimos propiedad para guardar la SAML que nos remite Sistema origen MINISTERIOProperty CabeceraSAML As%String(MAXLEN = "");
}

With the previous changes we get the SAML Assertion in the SOAP Service and then send it to the Process as a String.

Third, we need to convert the SAML String to Object in the Web Service Client to send it to nthe Target System as follows:

Include Ensemble

Class WSCLIENTE.RESNS.ConsultaRecetas Extends%SOAP.WebClient [ ProcedureBlock ]
{

/// This is the URL used to access the web service./// This is the namespace used by the ServiceParameter NAMESPACE = "urn:ihe:iti:xds-b:2007";/// Use xsi:type attribute for literal types.Parameter OUTPUTTYPEATTRIBUTE = 0;/// Determines handling of Security header.Parameter SECURITYIN = "ALLOW";/// This is the name of the ServiceParameter SERVICENAME = "RespondingGateway_Service";/// This is the SOAP version supported by the service.Parameter SOAPVERSION = 1.2;
Method RespondingGatewayCrossGatewayQuery(RequestSlotList As EsquemasDatos.RESNS.rim.SlotListType, ResponseOption As EsquemasDatos.RESNS.query.ResponseOptionType, AdhocQuery As EsquemasDatos.RESNS.rim.AdhocQueryType(REFELEMENTQUALIFIED=1,REFNAMESPACE="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0",XMLREF=1), federation As%xsd.anyURI(XMLPROJECTION="attribute"), ByRef startIndex As%Integer(XMLPROJECTION="attribute"), maxResults As%Integer(XMLPROJECTION="attribute"), id As%xsd.anyURI(XMLPROJECTION="attribute"), comment As%String(XMLPROJECTION="attribute"), federated As%Boolean(XMLPROJECTION="attribute"), Output ResponseSlotList As EsquemasDatos.RESNS.rim.SlotListType, Output RegistryErrorList As EsquemasDatos.RESNS.rs.RegistryErrorList(REFELEMENTQUALIFIED=1,REFNAMESPACE="urn:oasis:names:tc:ebxml-regrep:xsd:rs:3.0",XMLREF=1), Output RegistryObjectList As EsquemasDatos.RESNS.rim.RegistryObjectListType(REFELEMENTQUALIFIED=1,REFNAMESPACE="urn:oasis:names:tc:ebxml-regrep:xsd:rim:3.0",XMLREF=1), Output requestId As%xsd.anyURI(XMLPROJECTION="attribute"), Output totalResultCount As%Integer(XMLPROJECTION="attribute"), Output status As%xsd.anyURI(XMLPROJECTION="attribute"), CabeceraSAML As%String(MAXLEN="")) [ Final, ProcedureBlock = 1, SoapBindingStyle = document, SoapBodyUse = literal, SoapTypeNameSpace = "urn:oasis:names:tc:ebxml-regrep:xsd:query:3.0", WebMethod ]
{
 $$$LOGALERT("Dentro de WSCLIENTE.RESNS.ConsultaRecetas la CabeceraSAML: "_CabeceraSAML)
 do##class(Ens.Util.XML.Reader).ObjectFromString(.objetoSAML,CabeceraSAML,"%SAML.Assertion",)
 $$$LOGALERT("Dentro de WSCLIENTE.RESNS.ConsultaRecetas el objetoSAML: "_objetoSAML)
 
 set x509alias = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","aliasCertMSSSI")
 set password = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","pwdCertMSSSI")
 Set credset = ##class(%SYS.X509Credentials).GetByAlias(x509alias,password)
 set ref=$$$KeyInfoX509Certificate
 set assertion=##class(%SAML.Assertion).CreateX509(credset,ref)
 ;do ..SecurityOut.AddElement(assertion)do..SecurityOut.AddElement(objetoSAML) 

 Do (..WebMethod("RespondingGatewayCrossGatewayQuery")).Invoke($this,"urn:ihe:iti:2007:CrossGatewayQuery",.RequestSlotList,.ResponseOption,.AdhocQuery,.federation,.startIndex,.maxResults,.id,.comment,.federated,.ResponseSlotList,.RegistryErrorList,.RegistryObjectList,.requestId,.totalResultCount,.status)
}
}

Where the lines which convert the String back to a %SAML.Assertion object are:

 do ##class(Ens.Util.XML.Reader).ObjectFromString(.objetoSAML,CabeceraSAML,"%SAML.Assertion",)
 

And the ones which are supposed to send it as saml:Assertion inside SOAP:Header are:

 set x509alias = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","aliasCertMSSSI")
 set password = ##class(Util.TablasMaestras).getValorMaestra("PARAMETROS","pwdCertMSSSI")
 Set credset = ##class(%SYS.X509Credentials).GetByAlias(x509alias,password)
 set ref=$$$KeyInfoX509Certificate
 set assertion=##class(%SAML.Assertion).CreateX509(credset,ref)
 ;do ..SecurityOut.AddElement(assertion)
 
 do ..SecurityOut.AddElement(objetoSAML)

However when we generate a Log SOAP, we do not observe the saml:Assertion being added .  In fact it only shows the following content inside SOAP Header:

Output from Web client with SOAP action = urn:ihe:iti:2007:CrossGatewayQuery
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelopexmlns:SOAP-ENV='http://www.w3.org/2003/05/soap-envelope'xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'xmlns:s='http://www.w3.org/2001/XMLSchema'xmlns:wsa='http://www.w3.org/2005/08/addressing'><SOAP-ENV:Header><wsa:Action>urn:ihe:iti:2007:CrossGatewayQuery</wsa:Action><wsa:MessageID>urn:uuid:F5355A1C-F948-11ED-B9D9-005056AAA48E</wsa:MessageID><wsa:ReplyTo><wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address></wsa:ReplyTo><wsa:To>...</wsa:To></SOAP-ENV:Header><SOAP-ENV:Body>
 ...
    </SOAP-ENV:Body></SOAP-ENV:Envelope>

How would you recommend us to send the SAML as an assertion inside the SOAP Header to the target system from a SOAP Bussiness Operation?

Thans for answering this question

How would you recommend us to send the SAML as an assertion inside the SOAP Header to the target system from a SOAP Bussiness Operation?

Thanks for reading and replying.

Yone Moreno · Jun 5, 2023 go to post

Could you help us?

We have previously tried to set directly from the visual Data Transformation Language editor to set both last and nextUpdate time as follows:

Class Transformaciones.RESNS.FiltrarFechasyMotivoNoDispensacionAntesDepurarTransf06052023 Extends Ens.DataTransformDTL [ DependsOn = Mensajes.Response.RESNS.RespondingGatewayCrossGatewayRetrieveResponse ]
{

Parameter IGNOREMISSINGSOURCE = 1;Parameter REPORTERRORS = 1;Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;

XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='Mensajes.Response.RESNS.RespondingGatewayCrossGatewayRetrieveResponse' targetClass='Mensajes.Response.RESNS.RespondingGatewayCrossGatewayRetrieveResponse' create='new' language='objectscript' >
	<assign value='source.RegistryResponse' property='target.RegistryResponse' action='set' />
	<assign value='source.DocumentResponse' property='target.DocumentResponse' action='set' />
	<assign value='source.Solicitante' property='target.Solicitante' action='set' />
	<assign value='source.Action' property='target.Action' action='set' />
	<assign value='source.MessageId' property='target.MessageId' action='set' />
	<assign value='source.RelatesTo' property='target.RelatesTo' action='set' />
	<assign value='source.paciente' property='target.paciente' action='set' />
	<foreach property='source.RegistryObjectList.ExtrinsicObject()' key='k1' >
		<foreach property='source.RegistryObjectList.ExtrinsicObject.(k1).Slot()' key='k2' >
			<if condition='( source.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).name = "lastUpdateTime" )' >
				<true>
					<code>
						<![CDATA[ $$$LOGALERT("Entra lastUpdateTime")]]>
					</code>
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).ValueList.Value.(1)' property='lastUpdateTime' action='set' />
					<code>
						<![CDATA[ $$$LOGINFO("lastUpdateTime: "_lastUpdateTime)]]>
					</code>
					<assign value='lastUpdateTime' property='target.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).ValueList.Value.(1)' action='set' disabled='1' />
					<assign value='lastUpdateTime' property='target.RegistryObjectList.ExtrinsicObject.(1).Slot.(1)' action='set' />
					<code>
						<![CDATA[ $$$LOGASSERT("Después de set lastUpdateTime: "_lastUpdateTime)]]>
					</code>
				</true>
			</if>
			<if condition='( source.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).name = "urn:es:ms:ereceta:names:md:nextUpdateTime" )' >
				<true>
					<code>
						<![CDATA[ $$$LOGWARNING("Entra nextUpdateTime")]]>
					</code>
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).ValueList.Value.(1)' property='nextUpdateTime' action='set' />
					<code>
						<![CDATA[ 
  $$$LOGINFO("nextUpdateTime: "_nextUpdateTime)]]>
					</code>
					<assign value='nextUpdateTime' property='target.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).ValueList.Value.(1)' action='set' disabled='1' />
					<assign value='nextUpdateTime' property='target.RegistryObjectList.ExtrinsicObject.(2).Slot.(1)' action='set' />
					<code>
						<![CDATA[ $$$LOGASSERT("Después de set nextUpdateTime: "_nextUpdateTime)]]>
					</code>
				</true>
			</if>
		</foreach>
	</foreach>
</transform>
}

}

However, the previous attempt did not work because of it does assign lastUpdateTime and nextUpdateTime but it does not put them in the target message...

Currently we have developed and tested this other approach and it does work as intendeed, however; how could we simplify and/or make it more understandable?

Class Transformaciones.RESNS.FiltrarFechasyMotivoNoDispensacion Extends Ens.DataTransformDTL [ DependsOn = Mensajes.Response.RESNS.RespondingGatewayCrossGatewayRetrieveResponse ]
{

Parameter IGNOREMISSINGSOURCE = 1;Parameter REPORTERRORS = 1;Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;

XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='Mensajes.Response.RESNS.RespondingGatewayCrossGatewayRetrieveResponse' targetClass='Mensajes.Response.RESNS.RespondingGatewayCrossGatewayRetrieveResponse' create='existing' language='objectscript' >
	<assign value='##class(EsquemasDatos.RESNS.rim.RegistryObjectListType).%New()' property='target.RegistryObjectList' action='set' />
	<assign value='0' property='indiceExtrinsicObjectCreados' action='set' />
	<assign value='0' property='indiceSlotsCreados' action='set' />
	<foreach property='source.RegistryObjectList.ExtrinsicObject()' key='k1' >
		<foreach property='source.RegistryObjectList.ExtrinsicObject.(k1).Slot()' key='k2' >
			<if condition='( source.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).name = "lastUpdateTime" )' >
				<true>
					<code>
						<![CDATA[ $$$LOGALERT("Entra lastUpdateTime")]]>
					</code>
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).ValueList.Value.(1)' property='lastUpdateTime' action='set' />
					<code>
						<![CDATA[ $$$LOGINFO("lastUpdateTime: "_lastUpdateTime)]]>
					</code>
					<code>
						<annotation>Creamos Slot lastUpdateTime</annotation>
						<![CDATA[ 
  
  set extrinsicObject = ##class(EsquemasDatos.RESNS.rim.ExtrinsicObjectType).%New()
  
  do target.RegistryObjectList.ExtrinsicObject.Insert(extrinsicObject)
  
  set indiceExtrinsicObjectCreados = target.RegistryObjectList.ExtrinsicObject.Count()
  
  set slot = ##class(EsquemasDatos.RESNS.rim.SlotType1).%New()
  
  set slot.name = "lastUpdateTime"do target.RegistryObjectList.ExtrinsicObject.GetAt(indiceExtrinsicObjectCreados).Slot.Insert(slot)
  
  set indiceSlotsCreados = target.RegistryObjectList.ExtrinsicObject.GetAt(indiceExtrinsicObjectCreados).Slot.Count()

  set valueList = ##class(EsquemasDatos.RESNS.rim.ValueListType).%New()
  
  set target.RegistryObjectList.ExtrinsicObject.GetAt(indiceExtrinsicObjectCreados).Slot.GetAt(indiceSlotsCreados).ValueList = valueList

  do target.RegistryObjectList.ExtrinsicObject.GetAt(indiceExtrinsicObjectCreados).Slot.GetAt(indiceSlotsCreados).ValueList.Value.Insert(lastUpdateTime)
   
   ]]>
					</code>
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).id' property='target.RegistryObjectList.ExtrinsicObject.(indiceExtrinsicObjectCreados).id' action='set' />
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).home' property='target.RegistryObjectList.ExtrinsicObject.(indiceExtrinsicObjectCreados).home' action='set' />
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).lid' property='target.RegistryObjectList.ExtrinsicObject.(indiceExtrinsicObjectCreados).lid' action='set' />
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).objectType' property='target.RegistryObjectList.ExtrinsicObject.(indiceExtrinsicObjectCreados).objectType' action='set' />
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).status' property='target.RegistryObjectList.ExtrinsicObject.(indiceExtrinsicObjectCreados).status' action='set' />
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).mimeType' property='target.RegistryObjectList.ExtrinsicObject.(indiceExtrinsicObjectCreados).mimeType' action='set' />
					<code>
						<![CDATA[ $$$LOGASSERT("Después de set lastUpdateTime: "_lastUpdateTime)]]>
					</code>
				</true>
			</if>
			<if condition='( source.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).name = "urn:es:ms:ereceta:names:md:nextUpdateTime" )' >
				<true>
					<code>
						<![CDATA[ $$$LOGWARNING("Entra nextUpdateTime")]]>
					</code>
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Slot.(k2).ValueList.Value.(1)' property='nextUpdateTime' action='set' >
						<annotation>Añadimos Slot nextUpdateTime</annotation>
					</assign>
					<code>
						<![CDATA[ 
  $$$LOGINFO("nextUpdateTime: "_nextUpdateTime)]]>
					</code>
					<code>
						<![CDATA[ 

  set slot = ##class(EsquemasDatos.RESNS.rim.SlotType1).%New()
  
  set slot.name = "nextUpdateTime"do target.RegistryObjectList.ExtrinsicObject.GetAt(indiceExtrinsicObjectCreados).Slot.Insert(slot)
  
  set indiceSlotsCreados = target.RegistryObjectList.ExtrinsicObject.GetAt(indiceExtrinsicObjectCreados).Slot.Count()

  set valueList = ##class(EsquemasDatos.RESNS.rim.ValueListType).%New()
  
  set target.RegistryObjectList.ExtrinsicObject.GetAt(indiceExtrinsicObjectCreados).Slot.GetAt(indiceSlotsCreados).ValueList = valueList

  do target.RegistryObjectList.ExtrinsicObject.GetAt(indiceExtrinsicObjectCreados).Slot.GetAt(indiceSlotsCreados).ValueList.Value.Insert(nextUpdateTime)
   
   ]]>
					</code>
					<code>
						<![CDATA[ $$$LOGASSERT("Después de set nextUpdateTime: "_nextUpdateTime)]]>
					</code>
				</true>
			</if>
		</foreach>
	</foreach>
	<foreach property='source.RegistryObjectList.ExtrinsicObject()' key='k1' >
		<foreach property='source.RegistryObjectList.ExtrinsicObject.(k1).Classification()' key='k3' >
			<code>
				<![CDATA[ 
  set node = source.RegistryObjectList.ExtrinsicObject.GetAt(k1).Classification.GetAt(k3).nodeRepresentation]]>
			</code>
			<code disabled='1' >
				<![CDATA[ 
  $$$LOGINFO("node: "_node)]]>
			</code>
			<code disabled='1' >
				<![CDATA[ 
  $$$LOGALERT("$FIND(node, 00): "_$FIND(node, "00"))]]>
			</code>
			<if condition='( $FIND(node, "00") = 3)' >
				<annotation>Si nodeRepresentation empieza por &quot;00&quot;</annotation>
				<true>
					<code>
						<![CDATA[ 
  $$$LOGWARNING("sí empieza por 00 el nodeRepresentation")]]>
					</code>
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Classification.(k3).Slot.(1).ValueList.Value.(1)' property='nodeValue' action='set' />
					<code>
						<![CDATA[  $$$LOGWARNING("nodeValue: "_nodeValue)]]>
					</code>
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Classification.(k3).Name.LocalizedString.(1).value' property='localizedStringValue' action='set' />
					<code>
						<![CDATA[  $$$LOGALERT("localizedStringValue: "_localizedStringValue)]]>
					</code>
					<code>
						<annotation>Generamos y asignamos Slot y Name del motivo NO dispensación</annotation>
						<![CDATA[ 
  
  set classification = ##class(EsquemasDatos.RESNS.rim.ClassificationType).%New()
  
  do target.RegistryObjectList.ExtrinsicObject.GetAt(k1).Classification.Insert(classification)
  
  set indiceClassificationCreados = target.RegistryObjectList.ExtrinsicObject.GetAt(k1).Classification.Count()
  
  set slot = ##class(EsquemasDatos.RESNS.rim.SlotType1).%New()
  
  set slot.name = "codingScheme"do target.RegistryObjectList.ExtrinsicObject.GetAt(k1).Classification.GetAt(indiceClassificationCreados).Slot.Insert(slot)
  
  set indiceSlotsCreados = target.RegistryObjectList.ExtrinsicObject.GetAt(indiceExtrinsicObjectCreados).Slot.Count()

  set valueList = ##class(EsquemasDatos.RESNS.rim.ValueListType).%New()
  
  set target.RegistryObjectList.ExtrinsicObject.GetAt(k1).Classification.GetAt(1).Slot.GetAt(1).ValueList = valueList

  do target.RegistryObjectList.ExtrinsicObject.GetAt(k1).Classification.GetAt(1).Slot.GetAt(1).ValueList.Value.Insert(nodeValue)
   
  set name = ##class(EsquemasDatos.RESNS.rim.InternationalStringType).%New()
  
  set localizedString = ##class(EsquemasDatos.RESNS.rim.LocalizedStringType).%New()
  
  set localizedString.value = localizedStringValue
  
  do name.LocalizedString.Insert(localizedString)
  
  set target.RegistryObjectList.ExtrinsicObject.GetAt(k1).Classification.GetAt(1).Name = name

]]>
					</code>
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Classification.(k3).id' property='target.RegistryObjectList.ExtrinsicObject.(k1).Classification.(1).id' action='set' />
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Classification.(k3).classificationScheme' property='target.RegistryObjectList.ExtrinsicObject.(k1).Classification.(1).classificationScheme' action='set' />
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Classification.(k3).classifiedObject' property='target.RegistryObjectList.ExtrinsicObject.(k1).Classification.(1).classifiedObject' action='set' />
					<assign value='source.RegistryObjectList.ExtrinsicObject.(k1).Classification.(k3).nodeRepresentation' property='target.RegistryObjectList.ExtrinsicObject.(k1).Classification.(1).nodeRepresentation' action='set' />
				</true>
			</if>
		</foreach>
	</foreach>
</transform>
}

}

🤔👁👁🌟🐢🐝🚀👁👁🤔

how could we simplify and/or make it more understandable?

Thanks for your replies.