Article
· 7 hr ago 6m read

REST Service in IRIS Production: When You Crave More Control Over Raw DataContestant

 

  “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:

  1. Should we grovel before Intersystems and ask them to fix the code?
  2. Fix it ourselves with rusty tools and questionable ethics?
  3. Accept the current status quo and build something clunky but functional?
  4. ?

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.
 
So, here’s a question for you:
Results:
0%
(0 votes)
Should we grovel before Intersystems and ask them to fix the code?
20%
(1 vote)
Fix it ourselves with rusty tools and questionable ethics?
80%
(4 votes)
Accept the current status quo and build something clunky but functional?
0%
(0 votes)
?
All votes: 5.
Discussion (3)3
Log in or sign up to continue

1. Sound a perfect candidate for the InterSystems Ideas portal, to be able to do searches inside streams.
2. Another option:  you could use request/response messages that stores in normal properties (e.g. Extends Ens.Request or Ens.Response), you could convert those properties back to JSON or (compressed) stream at the BS level (after the response), so all your messages inside Ensemble could be searchable.

In the past what I have done is define a Web Application with a REST Dispatch class and in that class serialize the data from the JSON payload into a real message class with properties.  In this way the Business Service receives a request object with the properties defined and now the Message Viewer has the expected functionality.  This also means that the business service is not purlely a tied to REST.  Anyone that sends a proper pRequest object can use the business service.

Hi Stephen, this is a valid approach however you will lose information about problematic calls such as json can't be parsed or API key in headers is wrong. I prefer to actually save the http call in full as http generic message and then call a process or operation with transformation to the proper message format, as Alex demonstrated in his third example. You can still call operation manually if you want, passing the parsed message.