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.

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 Service
Parameter 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 Service
Parameter 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.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

 set ..MTOMRequired = 0
 set 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 MINISTERIO
Property 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 Service
Parameter 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 Service
Parameter 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:Envelope xmlns: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.

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.

🙌 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? 🤔

🧐 Additionally we have read 💭💭 carefully:

https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI...

https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI...

https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI...

https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI...

https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI...

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

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?