Hi @Evgeny Shvarov 

I don't have anything immediately to hand as I still feel that the reuse of code in Step 3 from the original class method is not best practice, although I do have this running in a live production for 2-3 operations where this was needed.

I will try and see if I can get something put together that can be exported and put onto the Open Exchange. Just a warning, I'm not one for Docker, so it'll be a Production deployment export smiley

Hey Yuri.

The users are held within the SQL table "Security.Users" in the %SYS namespace, so you could use embedded sql to return the information, however as you're unlikely to be executing your code directly from the %SYS namespace, I'd suggest creating a function that you pass the email address, and it returns the username.

Something like:

Class Demo.Utils.General.Users
{

ClassMethod UserFromEmail(Email As %String, Output Username As %String) As %Status
{
	//Initially set this to null, as we want to return it empty when we get no results
	Set Username = ""
	//Hold the Namespace within a variable so we can use the variable to set the namespace back once the SQL has been run.
	Set CurrNamespace = $NAMESPACE
	//Change NameSpace to %SYS
	Set $NAMESPACE = "%SYS"
	//Run query to get the Username based on the email address - note the use of the UPPER function to remove issues with case sensitivity
	&SQL(
	Select ID into :Username
	FROM Security.Users
	WHERE UPPER(EmailAddress) = UPPER(:Email)
	)
	
	//Set namespace back to the namespace the function was run from
	Set $NAMESPACE = CurrNamespace
	
	//Evaluate SQLCODE for result
	//Less than 0 is an error.
	If SQLCODE <0{
		WRITE "SQLCODE="_$SYSTEM.SQL.Functions.SQLCODE(SQLCODE)
		QUIT 0
		}
	
	//Greater than 0 can really only mean Code 100, which is no results found.
	If SQLCODE > 0 {
		QUIT 1 //No Result Found
		}
	Else {
		QUIT 1 //Result Found
		}
}

}
DEMO> WRITE Class(Demo.Utils.General.Users).UserFromEmail("YuriMarx@ACME.XYZ",.Output)
1

DEMO> WRITE Output
YMARX

This is by no means perfect as I have thrown it together for the example - please forgive the messy if/else's! smiley

There's a few "gotchas" when it comes to Character Encoding. But the key thing in you case is understanding the character encoding being used by the receiving system. This should be something specified in the specification of the receiving system, but many times it's not.

If I had to guess, it's most likely that the receiving system is using UTF-8 simply because latin1/ISO-8859-1 encodes the pound symbol as hex "A3" whereas UTF-8 encodes to  "C2 A3". As there's no solitary "A3" in UTF-8, there's nothing to print, which is why you get the ? instead. I'm sure there's other character sets where this can happen, but I would start there.

Hey Joe.

To quickly answer your second question: you will want to take a look at the table Ens.MessageHeader which contains the Session ID, and then the Ens.MessageBody which is linked to the Ens.MessageHeader on the field "MessageBodyId".

Hi Thomas.

Are you trying to connect to the same API endpoint from Test and Live?

Is it possible that the end point is performing IP filtering so your request from the Live server is being rejected?

Hey Andy.

When you're copying the router from your production, it will continue to reference the same rules class in the settings as per:

After you have copied the Router, you will want to hit the magnifying glass and then use the "Save As" option in the Rule Editor. After you have done this, you can then go back to your Production and then change the rule assigned to your Router via the drop down to select the new rule.

Just make sure you create a new alias for your Rule on the "general" tab on the rule page. 

Hey William.

I'm pretty sure you just need to query the table Ens.MessageHeader.

This should give you the process by way of the column SourceConfigName, and the status of the discarded messages.

For example:

SELECT *
FROM Ens.MessageHeader
WHERE SourceConfigName = 'ProcessNameHere' AND Status = 'Discarded'

You may want to consider including a time range depending on the size of the underlying database.

I ended up extending EnsLib.HL7.Operation.TCPOperation and overwriting the OnGetReplyAction method.

From there, I coped the default methods content but prepended it with a check that does the following:

  • Check pResponse is an object
  • Loop through the HL7 message in hunt of an ERR segment
  • Checks value of ERR:3.9 against a lookup table

If any of the above fail, the response message is passed to the original ReplyCodeAction code logic, otherwise it quits with the result from the Lookup Table.

The use of the Lookup Table then makes adding/amending error actions accessible to the wider team rather than burying it within the ObjectScript, and having the failsafe of reverting to the original ReplyCodeAction logic keeps the operation from receiving an unexpected error and breaking as it has the robustness of the original method.

Hey Patty.

If you just simply need the empty NTE to be added in using the DTL, you can set the first field to an empty string to force it to appear.

For example:

Will give this:

Note that my example is simply hardcoding the first OBX repetition of every first repeating field with no care for the content. You will likely need to do a for each where you evaluate if the source NTE:1 has a value, and then only set to an empty string if there is no content in the source.

Hey Kev.

The main way to build upon this would be to use something like Prometheus and Grafana to pull data out and then display it in a human readable way, and it has been covered on the forums a few times.

However, if you were to upgrade past IRIS 2020, you should find that you are able to utilise System Alerting and Monitoring (SAM) in your environment.