“At the request of the survivors, the names have been changed. Out of respect for the dead, the REST has been told exactly as it occurred.” |
Warning: There’s an important poll at the end of this article. I’m eagerly awaiting your responses!
So, we hit a snag. A big one. There’s no way to search messages in our production.
Sure, a message comes into the REST service, and everything in the production runs smoothly as it should.
Here’s a sample request:
{
"case":"2-12-85-06",
"to":"management of ZOO",
"subject":"Children report that the horse has grown a fifth leg"
}
Everything looks great in production!
BUT! Then either we or the client wants more:
"Hey, can we filter messages by specific criteria? For example, did we ever get anything related to case number 2-12-85-06?"
And that's where things took a sinister turn.
The standard message type, EnsLib.HTTP.GenericMessage, stores JSON data in streams. A great idea, right? Until it’s not.
Because searching through messages in streams works about as well as a flyscreen door on a submarine. Spoiler: It doesn’t. Like, at all.
You’ve probably seen this error before:
This adorable little red “NOPE” is the vigilant gatekeeper of your data. Always on duty, always blocking your sweaty fingers from touching anything sensitive. A true protector! But also a huge headache.
The Fight Back
"The red tide, Lester. Life. We’re being fed crap, day after day - bosses, wives, whatever. It’s killing us. And if you don’t stand up, if you don’t show them you’re still an animal deep down, the tide will sweep you away." |
We don’t retreat, though. Never.
There are a few possible solutions. I personally went for a storage-limited one because I don’t want to keep extra stored data. I just want to stay within the realm of production messages.
So, here’s the plan:
We create a custom message class, inheriting from EnsLib.HTTP.GenericMessage. Something like this:
Class Production.Message.ZooMessage Extends EnsLib.HTTP.GenericMessage
{
Property RequestJSON As %String(MAXLEN = "");
Property CaseId As %String;
Index ZooMessageCaseIdIndex On CaseId;
}
Next, we need to pass this custom message to the operation and include additional search-ready data (message body and case number).
Solution 1: The “Not-So-Great Start”
“Don’t trust what comes out of the ocean. - But... we came from the ocean.” |
We create a service by inheriting from EnsLib.REST.GenericService, then add it to the production. Simple, right?
Class Production.Service.ZooREST Extends EnsLib.REST.GenericService
{
Parameter DOCCLASS = "Production.Message.ZooMessage";
}
Production:
<Item Name="Service.RESTver1" Category="" ClassName="Production.Service.ZooREST">
<Setting Target="Host" Name="TargetConfigName">Operation.RESTver1</Setting>
<Setting Target="Adapter" Name="Port">1984</Setting>
</Item>
Except it’s not.
The input message type stays the same.
Parameter DOCCLASS = "Production.Message.ZooMessage" didn’t work.
Solution 2: “Too Many Cooks”
“This is quite unusual… - Unusual? Once I found a human foot in the oven. That’s unusual. This is merely weird.” |
Turns out, the issue boils down to this single line of code in the OnProcessInput method:
Replacing it with:
Set tRequest=$classmethod(..#DOCCLASS,"%New",pRequestBody,,pRequestBody)
...would fix the issue. But this change would render the vendor’s code in subsequent IRIS updates useless. A dead-end option that leaves a bad taste in the mouth.
Solution 3: The “Dragon-Guarded Path”
“You could just get in your car and drive away. - And why’s that? - Because some roads you just shouldn’t take. Used to be maps would mark them, saying, ‘Here Be Dragons.’ Now they don’t, but the dragons are still there.” |
The idea? Pass the message to a router, transform it, and send it to the process.
Components needed:
- A router
- A transformation
- A pinch of something mysterious and arcane
Class Production.Rule.Router Extends Ens.Rule.Definition
{
XData RuleDefinition [ XMLNamespace = "http://www.intersystems.com/rule" ]
{
<ruleDefinition alias="" context="EnsLib.MsgRouter.RoutingEngine" production="Production.Test">
<ruleSet name="ZOO Router" effectiveBegin="" effectiveEnd="">
<rule name="ZOO Process Inbound Http" disabled="false">
<constraint name="source" value="Service.RESTver3"></constraint>
<when condition="1">
<send transform="Production.DataTransformation.ZOO" target="Operation.RESTver3"></send>
<return></return>
</when>
</rule>
</ruleSet>
</ruleDefinition>
}
}
Class Production.DataTransformation.ZOO Extends Ens.DataTransformDTL [ DependsOn = (EnsLib.HTTP.GenericMessage, Production.Message.ZooMessage) ]
{
Parameter IGNOREMISSINGSOURCE = 1;
Parameter REPORTERRORS = 1;
Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;
XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='EnsLib.HTTP.GenericMessage' targetClass='Production.Message.ZooMessage' create='new' language='objectscript' >
<assign value='source.HTTPHeaders' property='target.HTTPHeaders' action='set' />
<assign value='source.Stream.Read(1000000)' property='target.RequestJSON' action='set' />
<assign value='{}.%FromJSON(source.Stream).case' property='target.CaseId' action='set' />
</transform>
}
}
A bit bulkier, yes, but it works. No ritual dances required.
The Verdict?
“- Nobody killed a judge. We own all the judges. What'd be the point of killing one?” |
Honestly, I can’t decide. That’s why I’m writing this.
So, here’s a question for you:
- Should we grovel before Intersystems and ask them to fix the code?
- Fix it ourselves with rusty tools and questionable ethics?
- Accept the current status quo and build something clunky but functional?
- ?
Your call.
PS: Code from the examples above is available in this GitHub repository: https://github.com/banksiaglobal/http-rest-production
![]() |
Aleksandr Kolesov is a seasoned software developer from Banksia Global with over 30 years of experience in the tech industry, specializing in the InterSystems IRIS platform. With a deep understanding of high-performance data management and data intensive application architectures, he has played a pivotal role in developing and optimizing applications for enterprises across various sectors. Over the years, Alex has earned a reputation for his expertise in building scalable, efficient systems, as well as his passion for mentoring up-and-coming developers. When he's not coding or solving complex technical challenges, Alex enjoys exploring new technologies and contributing to the growing community of InterSystems developers. |
![]() |
Banksia Global is a leading technology consultancy that specializes in helping organizations harness the full potential of modern data management solutions. With a strong focus on the InterSystems IRIS platform, Banksia Global provides strategic guidance, system integration, and custom software development services to clients across various industries. Their expertise lies in delivering high-performance, scalable solutions that enable businesses to drive innovation, streamline operations, and unlock new opportunities. Known for its collaborative agile approach, Banksia Global works closely with clients to ensure that each solution is tailored to meet their specific needs, fostering long-term partnerships and sustainable growth. |