Article
· Jan 17 9m read

Creating an IRIS Cross Functional App in <150 Lines of Code

The Lo-Code Challenge

Imagine the scene.  You are working happily at Widgets Direct, the internet's premier retailer of Widgets and Widget Accessories.   Your boss has some devastating news, some customers might not be fully happy with their widgets, and we need a helpdesk application to track these complaints.   To makes things interesting, he wants this with a very small code footprint and challenges you to deliver an application in less than 150 lines of code using InterSystems IRIS.  Is this even possible?

Disclaimer: this article documents the building of a very basic application and omits nicities like Security and Error Handling in favour of brevity.   This application should only be used as a reference and not used for any production application.  This article uses IRIS 2023.1 as the data platform, not all features described are available in earlier versions

Step 1 - Define a Datamodel

We start by defining a new clean Namespace - with a Code and a Data database. While everything can be in 1 database, it's useful to split these to allow for data refreshes.

Our helpdesk system needs 3 basic classes: a Ticket object which can contain Actions to document the interactions between an staff advisor UserAccount and a customer contact UserAccount.  Let's define these with some basic properties:

19 Lines of Code and we have our full datamodel!  We have set 2 classes as Persistent so they can be saved to the database, and also inherit from %JSON.Adapter, which enables us to very easily import and export our objects in JSON format.  As a test, we set our first user up in Terminal, save it, and then verify that the JSONExport works correctly

This all looks good.  The boss left us a csv file with a list of staff and customers.   We could write some code to parse this and load it, but is there an easier way?

Step 2 - LOAD DATA

InterSystems IRIS provides a simple to use LOAD DATA statement in SQL which allows for the easy loading of data from a CSV file, including the options to parse headers and rename fields.  Let's use this to load our user table:

We can use the header labels to extract this data and load it through to the database like so:

All 300 rows have been imported with a single command.   These 4 more lines of code bring our running count up to 23 Lines of Code written.   We can quickly verify that these records look correct with a basic SQL select

We now have our starting data, let's build some basic APIs to allow a front end to be connected.  We will be building our API as a REST service serving and accepting JSON.

Step 3 - Build a REST API

InterSystems IRIS provides native REST support through inheritance of the %CSP.REST class, so we will create a REST.Dispatch class and inherit from %CSP.REST.   This class is composed of 2 sections: an XData UrlMap which maps the URLs and Verbs to methods, and the methods which are called by these Urls.

Our Minimum Viable Product requires 4 operations: retrieval of the user list for staff or customers, retrival of the last tickets raised, retrival of a single ticket by it's ID number, and finally the creation of a new ticket.   We define our verbs, then the methods.

GetUserList is a basic Embedded SQL Cursor which outputs the data directly in JSON.  We can then parse this with the native JSON functionality, push it into a JSON array and serve it as the response body.  We pass in the staff variable from the URL directly to the query to alter the context of the data.

TicketSummary is almost identical, but the query accesses the ticket table instead

TicketSummary is the simplest service.  We open the object by ID, and write the inbuilt %JSONExport to the output.  If the object fails to load, we instead write out an Error packet

Finally, our UploadTicket method is the most complex. We have to read the payload from the request object, parse it to JSON, then apply it to a new instance of a Ticket using %JSONImport.  We also set the OpenDate and OpenTime from the current time, rather than expecting these as input.  After a succesful save, we echo back the JSON representation of the object, or if the object fails to load, we instead return an error.

With these services we add another 60 Lines of Code to our total.  We are now up to 89 Lines of Code in total for this application

We now need to create a Web Application under Security>Applications.  This should be set to a REST type application, and the Classname should be set as the Dispatch class we just created (Note, you will need to grant an appropriate security role to this application so that it can access the code and data).  After saving, the REST services can now be called from the URL you defined

We can call the UserList to check

We are now ready to create some data.  Let's use our REST client to send a payload to the Ticket Creation service.  We supply a Keyword, Description, Advisor and Contact, and we get back the JSON of the ticket we have created including the OpenDate and the TicketId

We now have our Minimum Viable Product.  Using a front end form builder of our choice, we can now send and receive ticket information via our REST Services.

Step 4 - Interoperabliity Requirements

You have now built a basic Ticketing application in just 89 Lines of written Code.   Your boss must surely be impressed?   Yes, but he has some bad news.  You missed a requirement.   Widgets Direct has a special contract in French speaking regions and all tickets written in French must go to Mme Bettie Francis for initial review.   Luckily, you have a colleague who has followed Robert Luman's excellent Article on Python Natural Language Support and has created a REST service which can accept a text sample and identify the language.   Can we use InterSystems IRIS Interoperability to call this service and automatically update the advisor to Mme Francis when the text is French?

We need to start by creating some Message classes so we have a way to send and receive our requests.  We need a Request which will hold a ticket ID and the Sample Text, and a Response which will return the Language code and Description. These will inherit from Ens.Request and Ens.Response

6 more lines of written code bring us up to 95 LOC.  We now need to create our Operation, which will send the request to your colleague's service and retrieve the answer.  We define an Outbound operation, with properties for Server and URL, and expose these to the user configuration by including them in the SETTINGS parameter.   This will allow us to update the request easily if the server path changes.   We create a helper method to set up an HTTPRequest, and then use this to call the service and populate our response

27 more lines of code bring us over 100, we are now at 122 Lines written.   We now need to set up this class within our Ensemble production.  Go to the Production Configuration under Interoperabliity, and press the Add under the Operations Header.  Set up your operation with the class name and a display name

We can then click it to get to Settings, enter the server name and URL and enable the Operation. 

We now need a second operation which takes a ticket ID and sets the Advisor to a supplied User Account ID.  We need both a message and an Operation class, but in this case, we will not return a response, the operation will perform the task without feedback

Another 12 lines of code brings us to 134 Lines written.  We can add this Operation to the Production in the same way we added the Language Service, but in this case we don't have any configuration to set.

Next we need a router which can call the service, assess the response and optionally call the French Advisor Operation.  We go to Interoperability>Build>BusinessProcess and access the visual rule builder.  We first set our contexts for Request and Response and add a Call item.  We set our inputs and outputs to the Message classes we created and then map the Inputs using the Request Builder.  Ensure that the "Asynchronous" flag is unchecked, we want to wait for the response before proceeding.

We then add an "If" item to assess the Language code returned.  If it's "fr", then we want to call out to the FrenchAdvisor operation

Mme Francis is user ID 11, so we set our Call object to supply our AdvisorUpdate message to the FrenchAdvisor service, and use the request builder to pass through the TicketID, and a fixed value of 11 to the Advisor parameter

We can now add this to the Production by clicking Add under the Processes header, selecting the class and giving it a DisplayName "FrenchRouter". 

We now have our routing in place. We just need a Service to scan for new tickets and send them to the Router for Processing.  We define a Service class based on a SQL adapter as so (adding another 8 Lines of Code to our count):

We then add this to our Production as we did with the Operation and Process objects.  We need to do some setup for the SQL adapter.   We supply connection details via an ODBC DSN to the local database, and a basic SQL query which the Service will use to query the tickets on a timer set in the CallInterval setting.   This query is paired with the Key Field Name setting, which defines the unique key for the query, and prevents the resending of records which have already been sent

With this in place, we now have a full production which will scan for new tickets, pass the text to an external service to parse the language, and optionally reset the advisor based on the response.  Let's try this out!  We start by sending an English request, which is returned as TicketId 70.  We wait a second, and access this record via the GetTicket REST service, and the advisor is unchanged from the original request

Let's repeat with French Text

When we request Ticket 71, our advisor has been changed to Mme Francis, as we expected! We can check this in Interoperability by locating the message in Visual Trace, and verifying that the Operations were called as expected.

We are now at 142 lines of code written, and we have an InterSystems IRIS application which persists data, has loaded data using LOAD DATA, provides a basic REST API for Viewing and Editing data, and an advanced integration engine providing Descision Support based on calls to an external REST service.  Surely, no one could ask for more?

Step 5 - Asking for more: Analytics

Your application is a roaring success and data has been flooding in.   This valuable data requires expertise to access, and the management of Widgets Direct want to gain some insights.  Is it possible to provide interactive access to the data?

Using InterSystems IRIS Analytics we can provide easy and fast access to advanced data manipulation tools.   We first need to enable Analytics support on our internal web application: 

This enables us to use the Analytics section against our Namespace.  Start by opening Analytics>Architect.  Select New, and fill out the form to create an Analytics Cube for the Ticket class. 

Next, we can set up our Dimensions, and a basic Drilldown listing, using the Visual Builder.   This view is drag and drop.  A listing can also be created using an easy visual builder, to customise what the user sees when they interogate a data point

Once we have some basic setup, we can Save, Compile and Build the Cube.  This will set up all indices and enable the Cube for analysis in Analyzer.   Open the Analyzer to start playing with the data.   In the example show, we can easily compare Advisors against a Hierarchy of Year and Quarter filtered by the Contact being served.   Once a cell has been clicking, the binoculars icon can be pressed to invoke the Drilldown listing you created for further analysis and export

Conclusion

With just 142 Lines of Code, we have a basic but functionality modern BackEnd application, with supporting tools to allow for inter-application communication and advanced analysis.   This is an oversimplified implementation and should only be used as an example of the bare bones required to build a database application in IRIS.  As mentioned at the start of the article, this code is not production ready and in real world use, developers should refer to InterSystems IRIS documentation and best practices to ensure their code is robust, secure and scalable (none of which apply to this code base).  Happy coding!

Discussion (9)4
Log in or sign up to continue