No worries, OAuth is a complicated subject. If the OAuth server you're trying to define in IRIS doesn't support the "well-known" endpoint, then I think you'll have to enter the server description manually, as opposed to having IRIS fetch it by discovery. You can read about how to do that here:

The URL that the discovery request gets sent to is:


So you might try hitting that endpoint in Postman to see what comes back.

You might also try turning on ISCLOG to log what's happening.  I'm not seeing anything in the doc specifically on ISCLOG, though it is mentioned in the context of other topics, for example:

The value of ^%ISCLOG corresponds to the verbosity of the logs, where higher is more verbose.  I'm not sure how high the scale goes, but I'm pretty sure it's less than 10, so I always just set it to 10.

You can skip setting the "Category" subscript to log all categories.

And don't forget to turn off ISCLOG when you're done!

kill ^%ISCLOG


set ^%ISCLOG=0

Just wanted to give an update on option "b" in this comment, in case anyone reads this going forward. 

I think at one time ^%ISCLOG was used to both set the log level and store the log data.  Now ^%ISCLOG is only used to set the log level.  The log data is stored in ^ISCLOG (no "%") in the %SYS namespace.  So instead of doing:

zwrite ^%ISCLOG    // This is no longer the log global!!

You would instead do:

zwrite ^ISCLOG

In the %SYS namespace. (Or look at it in the Management Portal, I find it much easier to read that way.)

I couldn't find anything in the docs on ISCLOG specifically, but it is mentioned in the context of other subjects, for example:

Without being able to see your environment, it's difficult to say where the disconnect is or what would need to be tweaked to decode those characters correctly.  However, if you have an opportunity to manually process the HL7 data at any point as it flows through the system, then you may be able to call $ZConvert/$ZCVT on the encoded data to decode it:

USER>s str = $C(90,111,108,195,173,118,97,114,101,115)
USER>w str
USER>w $ZCVT(str, "I", "UTF8")

However, there should be a way to specify to your business service the encoding of input data so that it can decode the data for you.  I would have thought that this would be done with either the "Charset" or "Default Char Encoding" settings, but it sounds like you've already tried that.  I'm not sure why this wouldn't be working, but I'm fairly confident that this is how encoded data is supposed to be decoded, so it may be worth another look.

Hi Clayton, I don't know how you would map from one set of units to another in the data in UCR (that would probably need to be done prior to ingestion into HealthShare), but there is a mechanism in the HS Clinical Viewer that enables you to define a "calculated" observation, the value of which is calculated from other observations. The value of this observation is calculated as the patient's data is loaded into the Viewer Cache and only exists in the Viewer Cache, not in the patient's data in UCR.  If you look at <install-dir>\distlib\trak\misc\HS-Default-ObservationItem.txt, you'll see an example of how this can be done:

3141-9^Weight Measured^^^^N
// Body mass index calulations
// BMI = Weight (lb) / (Height (in) x Height (in)) x 703
39156-5^Body mass index^^^^C^([3141-9]/([8302-2]*[8302-2]))*703^2^
// Metric Calculation
// BMI = Weight (kg) / (Height (cm) / 100 x Height (cm) / 100) 
// 39156-5^Body mass index^^^^C^([3141-9]/([8302-2]/100*[8302-2]/100)) 

This file is used to pre-populate the "observation item" code table in the Viewer Cache when the Viewer namespace is reset.  What the file is doing here is creating a couple of standard observation items for weight and height.  Then it defines a "calculated" item for BMI that is defined as "(weight / height2)*703", where the "height" and "weight" inputs are references to items the appear elsewhere in this file.

So you should be able to use this to convert from one set of units to another, however - it sounds like your use case is to be able to normalize multiple input formats into a single output format, which I'm not sure if you can do with this mechanism.  That is, while it should be possible to define an item that converts inches to feet/inches and another that converts centimeters into feet/inches, I don't know if there is a way to define a single item that can convert both inches and centimeters into feet/inches.

I haven't used this feature extensively, though, so maybe it can do some things (like conditional logic) that I'm not aware of.  Someone from Trak might now.  The HS-Default-ObservationItem.txt file is a uniquely HealthShare concept, but the code table that it populates (User.MRCObservationItem) belongs to Trak.

Hi Bill, I was facing a similar (but not identical) issue not too long ago where I was trying set %response.Status in my REST handler class, only to have a different status code reflected in the response to the client.

The issue ended up being that the request to the REST handler class that I was working on was being forwarded from another REST handler class that extends EnsLib.REST.Service rather than %CSP.REST.  The EnsLib REST handler works a little differently.  The user code is supposed to write the output to a response stream, with the response headers being set as attributes of the stream, rather than setting headers of %response directly.

So my question is, is your REST handler downstream from an EnsLib.REST.Service REST handler?  I also see that your REST handler extends Ens.BusinessService.  I wonder if something in that class is overriding your %response headers.  Is there any way you can test your class with that superclass removed?

Hi Cyriak, you do not need to generate the Aggregation Key, as that is done automatically when the Access Gateway fetches a patient's data.

I believe the simplest way for custom code running in the Viewer to get the Aggregation Key value would be to first get the ID of the patient object in the Viewer Cache from the CSP session:

Set tViewerPatientID = $listget(%session.Data("lastPatientId"))

(For historical reasons the value of %session.Data("lastPatientId") is a $list, though it should only ever have a single value.)

Then you would call web.SDA3.Loader:GetAgKey(), passing it the ID of the patient object to get the Aggregation Key:

Set tAgKey = ##class(web.SDA3.Loader).GetAgKey(tViewerPatientId)

GetAgKey() is documented as an API method, so it should be safe to use.

You are correct that you can then use the Aggregation Key to query for the current patient's data in the Aggregation Cache.

Does that answer your question?

Hi, with regard to your second question, if your property "CODE" is unique and indexed, ie, your class definition includes something like:

Index CodeIndex on CODE [Unique];

 Then there is a generated method you can use to open the object that has CODE=Xparameter:

set myObject = ##class(User.MyPersistentClass).CodeIndexOpen(Xparameter, concurrency, .sc)

For any unique index, the method name is always "[index-name]Open".  You can read more about auto-generated methods here:

With regard to your first question, I'm not aware of any system method that returns a list of all saved objects for a class, but you could implement that by creating a class method that creates and executes the appropriate query, then iterates over the results and copies them to a list.  I would be cautious about doing something like this with large tables though, since you would essentially be loading the entire table into memory.  This could lead to <STORE> errors:

A better solution might be to implement some kind of iterator method that only loads one object at a time.

Here's a routine I use to restart a particular host in the production:

    set tSC = ##class(Ens.Director).EnableConfigItem(pName,0) if 'tSC write !, $system.Status.GetOneErrorText(tSC), !! quit
    set tSC = ##class(Ens.Director).EnableConfigItem(pName,1) if 'tSC write !, $system.Status.GetOneErrorText(tSC), !! quit

Hi Conor,

The issue is that HealthShare does not support CORS requests against FHIR endpoints that are secured with standard Caché authentication.  So if you look at the Network tab in your browser's developer tools, you'll see that before your browser sends the GET request to that URL, it sends an OPTIONS request to that same URL.  HealthShare responds with a 404, and what the browser actually complains about is the response missing some headers that are expected in a CORS response.

For an endpoint to support CORS, the authentication in the CSP application settings for the endpoint has to be set to "Unauthenticated".  If you're just developing or trying out FHIR, this is fine.  In production, the expectation is that this endpoint would be secured via OAuth.

Hi Joao,

Probably what has happened is you've corrupted the resource storage by doing a physical delete against the storage table.  If you need to delete a resource in the resource repository, the correct way to do this is via the delete interaction:

STU3 is the current version of FHIR.  The version currently supported by HealthShare is DSTU2.

Hi Scott,

One way you could do this is via an MDM^T02 HL7 message.  There is actually an example message containing a PDF document distributed with HealthShare: <install-directory>\Data\Scenario_4.hl7

The document data is encapsulated in a series of OBX segments:


The critical pieces of this are:

OBX-2: Must be "ED" for "Encapsulated Data"

OBX-5.3: Must be the file type.  In this case it's "PDF".  If you want to view this document in the HealthShare Clinical Viewer, then you can find a list of supported doc types at websys.Document:ValidTypes in the Access Gateway namespace.

OBX-5.4: Must be "Base64" if the data is base64-encoded, otherwise it can be left blank.

OBX-5.5: The document data



I know of no such built in tool, however here's a routine I wrote to pretty print an XML string:

	for tI = 1:1:10 write ! }
	set tTabCount = 0
	// if starting with an XML prolog, do not increment tab on next line
	if ($E(pXML,1,5)="<?xml") 
		set tI = $FIND(pXML,">")
		set $EXTRACT(pXML,tI-1) = ">"_$C(13,10) 
	else set tI = 1 
	for {
		set tI = $FIND(pXML,"<",tI) quit:tI=0
		if ($EXTRACT(pXML,tI,tI+7)="![CDATA[") {
			//set tTabCount = tTabCount + 1
			do Tabs
			set $EXTRACT(pXML,tI-1) = tTabs_"<"
			set tI = $FIND(pXML,">",tI) quit:tI=0
			set $EXTRACT(pXML,tI-1) = ">"_$C(13,10)
			//set tTabCount = tTabCount - 1
		//open tag
		elseif ($EXTRACT(pXML,tI) '= "/") {
			do Tabs
			set $EXTRACT(pXML,tI-1) = tTabs_"<"
			set tI = $FIND(pXML,">",tI) quit:tI=0
			// only increment tab count if this is not an empty tag (<tag/>)
			if ($E(pXML,tI-2)'="/") set tTabCount = tTabCount + 1 }
			//if followed by another open tag, insert a newline before it
			if ($EXTRACT(pXML,tI) = "<") set $EXTRACT(pXML,tI) = $C(13,10)_"<" }
		//close tag
		else {
			set tTabCount = tTabCount - 1
			//if following another close tag, put tabs before this one ($C(62) = ">")
			if ($EXTRACT(pXML,tI-4,tI-2) = $C(62,13,10)) {
				do Tabs
				set $EXTRACT(pXML,tI-1) = tTabs_"<"
			set tI = $FIND(pXML,">",tI) quit:tI=0
			set $EXTRACT(pXML,tI-1) = ">"_$C(13,10)
	write pXML
	set tTabs = ""
	for i=1:1:tTabCount set tTabs = tTabs_$C(9) }