When an error has occurred, the return value is not 0. Instead, it is a %Status, and likely already contains the error information you need.

As to your actual question, finding (the id of) the response message header is not trivial. Ensemble Business Services have a %RequestHeader property from which you can take the CorrespondingMessageId, but unfortunately it is only populated if the call did not return an error status. It is still possible, though. You can use the id of the request body to find out what you need. It does need some SQL, though:

ClassMethod GetResponseHeaderId(RequestId As %String, Output sc As %Status) As %String
  &sql(SELECT CorrespondingMessageId INTO :ResponseHeaderId
         FROM Ens.MessageHeader
        WHERE MessageBodyId = :RequestId)
  If SQLCODE Set sc = $$$ERROR($$$SQLError, SQLCODE, $Get(%msg))
  Set sc = $$$OK
  Return ResponseHeaderId

With this helper method, you can open the response message header object. Assuming the request body is called CallReq:

  Set Id = ..GetResponseHeaderId(CallReq.%Id(), .sc)
  If 'sc Quit sc
  Set RspHdr = ##class(Ens.MessageHeader).%OpenId(Id, , .sc)
  If 'sc Quit sc
  $$$LOGINFO("Response is an error: "_RspHdr.IsError)


The ToXML function is broken. This is a known bug (I've reported it mid-Januari).

It is possible to fix it, but it requires patching system code, class HS.FHIR.DTL.Util.XML.Adapter to be precise.

In that class, at (on my system, 2021.2) line 359 (in method ToXMLHelper), there is this statement:

set isprimitive = ($extract(propType)="%")

This basically assumes all properties are objects, except those that start with a %-sign. This is obviously wrong. The code will work if you replace that statement with this:

set isprimitive = ($extract(propType)="%") || (propType="") || ($$$classIsDataType(propType))

Here we additionally check for properties without a type (DomainResource has one: property id), and typed properties that are datatypes. With this change, the ToXML() method works for me.


As you're using Ensemble, you can use class EnsLib.EDI.XML.Document. I don't have version 2015, but the following works on IRIS. I hope this is present in Ensemble 2015 as well. (Note that, annoyingly, method domGetValueAt is marked internal, and therefore doesn't show up in the documentation. I don't know how to achieve the same result without using this method.)

Set XML = "<a><b><c>some content</c></b></a>"
Set Path = "/a/b"
Set Doc = ##class(EnsLib.EDI.XML.Document).ImportFromString(XML, .sc)
If 'sc Quit $System.Status.DisplayError(sc)
Set sc = Doc.domGetValueAt(.Value, Path, "fo", 1)
If 'sc Quit $System.Status.DisplayError(sc)
Write Value,!

This outputs "<b><c>some content</c></b>" as you want.

Hope this helps,

Ah, I see -- yes, I can reproduce this, and I would think this is a bug. (It does work as expected in an "if", but not in a "case".) You may want to take this up with WRC, to see what they think.

As a workaround, are you aware that you can use local variables? You could assign the value of source.Items.(i) to a local variable, and use that in the case statement. Something like this:

In a switch statement like yours, this would probably save some typing as well. It does mean the lines in the DTL no longer display properly, though. (Although they might not anyway here.)


Studio behaves really annoyingly if it loses its TCP/IP connection, as you described. I don't know how to prevent that. You probably don't have to restart HealthShare, though. When the crash happens, don't close the dialog immediately. Instead, first remove all locks for the process Studio was connected to; it shouldn't be too hard to find. (Something like System Operation -> Locks -> Manage Locks, I don't have access to a HealthShare instance right now.) Then allow Studio to reconnect, which should now work without issues.

If you massage the timestamp format a bit so it is ISO8601/XSD compatible, you can use the XSDToLogical method of %TimeStamp to do the conversion for you:

DEV>Set ts = "2018-02-01 00:00:00+0600"
DEV>Set ts = $Translate(ts, " ", "T")
DEV>Set ts = $Extract(ts, 1, *-2)_":"_$Extract(ts, *-1, *)
DEV>Write ts
DEV>Write ##class(%TimeStamp).XSDToLogical(ts)
2018-01-31 18:00:00

I have not run into this problem. Also, it appears that Caché already adds the Content-Length header:

  Set req = ##class(%Net.HttpRequest).%New()
  Set req.Server = "www.google.com"
  Do req.EntityBody.Write("test")
  Do req.Put("/", 1)


PUT / HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; Cache;)
Host: www.google.com
Accept-Encoding: gzip
Content-Length: 4
Content-Type: text/html; charset=UTF-8

I suspect Content-Length isn't the actual issue. I don't have any ideas about what is, though. (Perhaps you should at least check if your Caché sends it.)