This is about what happened on my while I was studing the REST API on IRIS - A data platform with flxible interoperability
You may not know about me... In reality I am not a programmer and I am not good at coding... My interest is simply looking around and trying out some simple stuff (Sorry I cannot handle the complicate stuff)
As I am not a programmer, don't expect me can do some technical writing here. Come on, I know here is not a place for writing novel.....me, however, just simply want to write sth in my way.
- for my future review
- for sbd might have a chance who uderstand what I am talking about
- for sbd might have some inspiration after taking a glance at my simple work
- or for sbd simplly love reading a novel about a poor code writer trying out sth that she don't know
Here thank you for your reading (maybe I am the only one reading this story)
Recently, I was studing sth about IRIS and majorly focus on the interoperability. (you may see my pervious work was on pyodbc, and I have as few work on the Python NativeAPI after that, maybe share later)
Why playing on the interoperability? just simply sbd told me that it is easy to use. can connect to many diffierent systems with different protocrol/standard/communcationMethod SIMPLY
You know when a woman like me heard about many diffierent systems and SIMPLY, my eyes blink!!!
OK, I decided to start with sth common, the REST api
Just like joining a party, being a guest (client) is always more relax than being a host.
To be a guest, you just simply dress up, prepare some food, or take a brunch of flower and attend the party.
To be a host, hahahha tons of work to do... setup the place, prepare the F&B, deciede the run down, and the most tragic things ------- do the cleaning after party.
Conceptraly, things should be similar in building a Host or a client.
But for me, a poor program writer, I found that it is more difficult for me to build a REST client on IRIS than building a host
The reason is simply becasuse there are planty of resourse which teach you how to build a host (maybe they think building a host is a more complex concept, and you may need more guidience)
For me, however, for the simple thing, I need gudience also ...... I am not good a coding.. not even the relationship between HTTP and REST ok?
Although I found out an easy way to build a clinet, but it was not easy for me (at least for me) to find that way.
That's why triggered me to write down sth. I am pretty sure that I will forget totally in the future, if I didn't write down any hints for myself.
Session | Description | |
---|---|---|
1 | Setup a REST API host interface on IRIS | this allow you to access objects/table in IRIS directly |
2 | Connect the REST API interface to IRIS production | this allow you to commumate with the other system connected to IRIS within the production environment |
3 | Setup a REST API client interface on IRIS production | this allow the other systems connected to IRIS within the production environment talk to you |
You can easliy learn Session 1 and 2 from the online learning service from InterSystems by
Visit the course Setting Up RESTful Services
And it have quite a lot pages to talk about in on the online document
REST and JSON
But I must say sorry to the writter, I can undertand the word, I can understand the sentenses, but I don't know how to make it work!!!
So I went back to the online learning
But you can only find the information about Session 3 from the online document
Creating REST Operations in Productions
only 1 page .... maybe it is good enough for you... but it is tooo hard for me to understand what is happening....
Anyway, let's go
Before making a REST interface, I created an object class Company on my IRIS platfrom.
Initally, I really hope that to have place to store the WebURL if different compnay, just like a phonebook.
And I can search for them in the future, and actually I have made a UI for query, select and update these Company objects by Python tkinter and nativeAPI.
But suddently, I sth come up in my mind. Maybe, or might be, or it might likely to be, the data sbd found that the data inside my Company objects are very useful for them too!!!!
Ohhhhh it is such a good news! I should let them join my party and help to maintain the data!!
That's the reason I want to setup a REST API for accessing my Company objects
Class KLlibrary.BObj.Company Extends (%Persistent, %JSON.Adaptor)
{
Property Name As %String [ Required ];
Index NameIndex On Name;
Property WebURL As %String(MAXLEN = 300);
Property Description As %String(MAXLEN = 2000);
Property Remark As %String;
Property EstablishYear As %Integer;
}
You can see that my Company class extended 2 objects, %Persistent and %JSON.Adaptor
- %Persistent allows us to %New() the company objects and %Save() them in the IRIS database
- %JSON.Adaptor allows us to use the method under the %JSON.Adaptor, normally, it is not a must to include it.
In our case, we are going to work on the REST API and it is more easy to package our data into a JSON string and send it out (or take them in). So it's better have this little tools
So now, I have a company class on IRIS
graph TD;
subgraph IRIS;
Company
end;
To make the REST interface for accessing my company objects, we will go through the following steps
- Step1 : make a API specification
- Step2 : upload the API specification to IRIS
- Step3 : create a web application on IRIS
- Step4 : implement the API operation method
- Step5 : .... seems no more, maybe play around and enjoy...
Seems very simple, right, maybe... yes.. if you know the trick.
The very begining step, we should prepare a API spec file to describe what is you REST api looks like
What I heard is that there is a standard Swagger Standard for writing a spec file.
If you are using this standard to make a spec file, it is more likely that your spec can be read easily by most of the API management software in the market (I guess, otherwise, why do we need a standard??).
I have prepared mine here klAPI.spec.json
I am sorry to let you know that this is my 1st time to write a API spec (yes API spec, not specific to REST). I was so excited and just want to take some of the code to here and explain what I am doing.
Let's talk about sth inside the path defination
I think that you might know that there are a few basic elements in the REST api for helping you to describe what kind of actions you want to do
element | In my point of view |
---|---|
path | it looks like to tell the host what is the target of this action |
method | e.g. "get", "post", "put", "delete", tell the host what kind of operation you are going to do with this target |
parameters | tell the host more specific stuff about this target. just take an example, if you tell your dating agent that I want to get (method) boys (path). You agent might introduce all the boys on the list to you. Do you think you have time to date them all? So now, you need to say long hair (parameter) plx. Then the result will be much more specific. |
body | this one will be useful if you want to update some data about your target. an example, your agent introduced a long hair boy and you found that he cut his hair recently. You may call your agent and update the status of the boy in the conversation (body) |
So now lets move on to my work. In my API spec [klAPI.spec.json](https://github.com/ecelg/My-study-about-REST-API-on-IRIS/blob/main/klAPI.spec.json).
I defined the following operations
path | method | parameter | body | |
---|---|---|---|---|
/allcompanies | get | by using this operation a list of all companies data will be return in JSON format | ||
/newcompany | post | a schema definitions of Company | Add a new compnay object. puting a String in JSON format to describe the company that you are going to add. and post it to the host | |
/company | get | id | get the data of a specify company by its id, the data of the target company will be return in JSON format | |
/company | post | id | a schema definitions of Company | the data of a specify company with the specifi id will be update according tho the JSON string you upload |
/company | delete | id | delete the compnay object by its id, i don't like it |
so what is a schema definitions of Company, it looks like the following
it descibe the what is my company object looks like
"definitions":{
"Company":{
"type":"object",
"properties":{
"Name":{
"type":"string"
},
"WebURL":{
"type":"string"
},
"Description":{
"type":"string"
},
"Remark":{
"type":"string"
},
"EstablishYear":{
"type":"integer"
}
}
}
So when we want to talk about it in JSON fromat, we can follow the above standard and to make sth in the body
For example
{
"Name": "InterSystems",
"WebURL": "https://www.intersystems.com/",
"Description": "",
"Remark": "",
"EstablishYear": 1978
}
Ok the spec is ready! Let's move on to the next step
This part is pretty easy, but we need a little tools to help us. You might have your favour tool... but for me... I only know one man, who is Postman.
In here I won't explain much about this man, as I am not very familiar with him...
For me, he is doing a great job especially when I testing my API (I can get sth from browser, but i don't know how to post sth by browser. He helps.)
Anyway, come back to the topic. I was told that
you can query the API Management service in your instance of InterSystems IRIS. Send the following HTTP GET request, replacing [YourServer] with the address of your server (and maybe port also)
GET http://[YourServer]/api/mgmnt/
basic Authentiction is required
In order to test if my API Management service is working. Let's check it out by my Postman (if the API Management service is not working, we cannot upload our API spec)
1st, I setup a basic authorization with my IRIS login and password
As my postman is living in the same host of IRIS with port 52773
So I Get the path http://localhost:52773/api/mgmnt/
We can see some information of the interfaces return.. so I assume it should be working.
Now, we can upload our spec!!
POST the path to http://localhost:52773/api/mgmnt/v2/KATEDB/klAPI
KATEDB <-- Namespace
klAPI <-- the folder which host the spec, I will show you in a moment
and put my spec (prepared in Step1) into the body, remember to set the fromat to JSON
Three files are generated a after posting the spec
File | Use for |
---|---|
klAPI.spec.cls | the API spec that we upload you can update this file to modifiy the feature of the API |
klAPI.disp.cls | auto generate class file based on the spec it will re-gen after you recomplile the spec class it helps you the call the correct functio in the XXX.impl.cls class file base on the Method and Path this file seems not for editing |
klAPI.impl.cls | auto generate class file based on the spec Important!! it will re-gen after you recomplile the spec class (some of you argeument setting will missing after regen) You should edit this class file to implement the function that you would like to achieve |
Let's go back to the klAPI.impl.cls file in Step4
This part is quite stright forward
Open the management portal
System Administration > Security > Applications > Web Applications
Create New Web Appliction
Input the Name (Important! this will become part of your api URL)
Choose the Namespace
Check Enable Application
Check REST
Input the Dispatch Class classname of the .disp.cls (in my example "klAPI.disp")
Check the Allow Authentcation Method
Click Save
Yeah!!! This part is finished!
In this part, you may need a little bit knowledge of the object script or you can do it in Embedded Python (I am sorry I am still laerning about embedded python, but I am sure I will share what I learnt soon)
I have uploaded my klAPI.impl.cls for you reference.
You may have a better way to write the function, but I want to take part of my code for discussion here
At the beginning of the code, we have extended 2 classes
- %REST.Impl (generated by default)
- Ens.Rule.FunctionSet (not necessary in this session, added by me in order for leting the business rule can use the function of this class. I will explain in session 2 )
Class klAPI.impl Extends (%REST.Impl, Ens.Rule.FunctionSet) [ ProcedureBlock ]
I won't go through the function one by one, so I pick the function EditCompanyById as an examply to explain how its work
Arguments | |
---|---|
id | id of the company (no default value) |
body | input data of the compnay in JSON format from the body of the REST message (no default value) |
prod | for switching the operation to the IRIS Interoperability Production, not using in this session. I set the default value to "". Important!! But the defalut value gone after I update the spec. Make sure to write back the default value after you update the spec. |
In the following code, you can see that id the prod is not 1 (prod =1 will pass to the Session2 part to handel), I open a **Company** object by the id, and that I import the data from the body by using the function **%JSONImport**. and then save the object. The data of the object is then updated.
Very easy right?
**Important** my **Company** object class should extends the **%JSON.Adaptor**, in order to use the very convinenec**%JSONImport** function.
/// Update existing company given ID and data. Returns updated company<br/>
/// The method arguments hold values for:<br/>
/// id, CompanyId<br/>
/// body, Company Info<br/>
/// prod<br/>
ClassMethod EditCompanyById(id As %Integer, body As %Stream.Object, prod As %Boolean = "") As %DynamicObject
{
//(Place business logic here)
//Do ..%SetStatusCode(<HTTP_status_code>)
//Do ..%SetHeader(<name>,<value>)
//Quit (Place response here) ; response may be a string, stream or dynamic object
if prod'=1
{
try
{
//check if the ID exisit
if '##class(KLlibrary.BObj.Company).%ExistsId(id)
{
do ..%SetStatusCode("400")
return {"ErrorMessage": "No company with this ID"}
}
//open the company object with the given ID
set company=##class(KLlibrary.BObj.Company).%OpenId(id)
//fill new Compnay with data from message body
do company.%JSONImport(body)
do company.%Save()
//fill export the compnay data to JSON format
do company.%JSONExportToString(.companyOut)
return companyOut
}
catch(ex)
{
do ..%SetStatusCode("500")
return {"ErrorMessage": "Server Error"}
}
}
if prod=1
{
//complete the request through the business process
try
{
return ..CallInterface("/company","POST",id,body)
}
catch(ex)
{
do ..%SetStatusCode("500")
return {"ErrorMessage": "Failed business process call"}
}
}
}
After we implemented all of the function in the klAPI.impl.cls, we can test it out by postman
list the following example
It's working!