IAM 2.3.3 added
This repository contains the materials and some examples you can use to learn the basic concepts of REST and IAM.
You can find more in-depth information in https://learning.intersystems.com.
You need to setup your access to InterSystems Container Registry to download IRIS limited access images.
Have a look at this Introducing InterSystems Container Registry on Developer Community.
docker login -u="user" -p="token" containers.intersystems.com
docker pull containers.intersystems.com/intersystems/iris:2021.1.0.215.0
docker pull containers.intersystems.com/intersystems/iam:2.3.3.2-1
IMPORTANT! Copy your InterSystems IRIS IAM enabled license file into the workshop root and rename it to iris.key
.
Build the image we will use during the workshop:
$ git clone https://github.com/intersystems-ib/workshop-rest-iam
$ cd workshop-rest-iam
$ docker-compose build
docker-compose up
superuser
/SYS
.%Persistent
and %JSON.Adaptor
.%JSON.Adaptor
and transforming objects to and from JSON, check this great article JSON Enhancements on Developer Community.^%REST
wizard.WEBINAR > do ^%REST
REST Command Line Interface (CLI) helps you CREATE or DELETE a REST application.Enter an application name or (L)ist all REST applications (L): L
Applications Web Applications
------------ ----------------
Enter an application name or (L)ist all REST applications (L): Webinar.API.Leaderboard.v1
REST application not found: Webinar.API.Leaderboard.v1
Do you want to create a new REST application? Y or N (Y): Y
File path or absolute URL of a swagger document.
If no document specified, then create an empty application.
OpenAPI 2.0 swagger: /https://github.com/intersystems-ib/workshop-rest-iam/blob/master/shared/leaderboard-api-v1.json
OpenAPI 2.0 swagger document: /https://github.com/intersystems-ib/workshop-rest-iam/blob/master/shared/leaderboard-api-v1.json
Confirm operation, Y or N (Y): Y
-----Creating REST application: Webinar.API.Leaderboard.v1-----
CREATE Webinar.API.Leaderboard.v1.spec
GENERATE Webinar.API.Leaderboard.v1.disp
CREATE Webinar.API.Leaderboard.v1.impl
REST application successfully created.
Create a web application for the REST application? Y or N (Y): Y
Specify web application name. Default is /csp/Webinar/API/Leaderboard/v1
Web application name: /leaderboard/api/v1
-----Deploying REST application: Webinar.API.Leaderboard.v1-----
Application Webinar.API.Leaderboard.v1 deployed to /leaderboard/api/v1
Webinar.API.Leaderboard.v1.impl
.ClassMethod addPlayer(body As %DynamicObject) As %DynamicObject
{
set player = ##class(Webinar.Data.Player).%New()
do player.%JSONImport(body)
set sc = player.%Save()
if $$$ISERR(sc) {
do ..%SetStatusCode(405)
quit ""
}
do player.%JSONExportToStream(.stream)
quit stream
}
ClassMethod getPlayers() As %DynamicObject
{
set sql = "SELECT Id, Name, Alias FROM Webinar_Data.Player order by Score"
set statement = ##class(%SQL.Statement).%New()
set sc = statement.%Prepare(sql)
set rs = statement.%Execute()
<span class="pl-k">set</span> <span class="pl-v">array</span> = []
<span class="pl-k">while</span> <span class="pl-v">rs</span>.<span class="pl-e">%Next</span>() {
<span class="pl-k">do</span> <span class="pl-v">array</span>.<span class="pl-e">%Push</span>(
{
<span class="pl-s"><span class="pl-pds">"</span>Id<span class="pl-pds">"</span></span>: (<span class="pl-v">rs</span>.<span class="pl-e">%Get</span>(<span class="pl-s"><span class="pl-pds">"</span>Id<span class="pl-pds">"</span></span>)),
<span class="pl-s"><span class="pl-pds">"</span>Name<span class="pl-pds">"</span></span>: (<span class="pl-v">rs</span>.<span class="pl-e">%Get</span>(<span class="pl-s"><span class="pl-pds">"</span>Name<span class="pl-pds">"</span></span>)),
<span class="pl-s"><span class="pl-pds">"</span>Alias<span class="pl-pds">"</span></span>: (<span class="pl-v">rs</span>.<span class="pl-e">%Get</span>(<span class="pl-s"><span class="pl-pds">"</span>Alias<span class="pl-pds">"</span></span>)),
<span class="pl-s"><span class="pl-pds">"</span>Node<span class="pl-pds">"</span></span>: (<span class="pl-en">$system</span>.<span class="pl-e">INetInfo</span>.<span class="pl-e">LocalHostName</span>())
})
}
<span class="pl-k">quit</span> <span class="pl-v">array</span>
}
ClassMethod getPlayerById(playerId As %Integer) As %DynamicObject
{
set player = ##class(Webinar.Data.Player).%OpenId(playerId)
if '$isobject(player) {
do ..%SetStatusCode(404)
quit ""
}
do player.%JSONExportToStream(.stream)
quit stream
}
ClassMethod updatePlayer(playerId As %Integer, body As %DynamicObject) As %DynamicObject
{
set player = ##class(Webinar.Data.Player).%OpenId(playerId)
if '$isobject(player) {
do ..%SetStatusCode(404)
quit ""
}
do player.%JSONImport(body)
do player.%Save()
do player.%JSONExportToStream(.stream)
quit stream
}
ClassMethod deletePlayer(playerId As %Integer) As %DynamicObject
{
set sc = ##class(Webinar.Data.Player).%DeleteId(playerId)
if $$$ISERR(sc) {
do ..%SetStatusCode(404)
}
quit ""
}
/leaderboard/api/v1
in Web Applications. Set unauthenticated access and set Webinar
temporal role.GET Player
, GET Players
, POST Player
y PUT Player
.You'll plug the API implementation into a production, so you could use any feature of interoperability productions withing a REST API implementation.
Webinar.Production
.Production Settings
and in Settings tab make sure Testing enabled
is checked.Webinar.BO.DummyREST
. Then, in the Settings tab configure:
mockbin.com
/request
(%REST.Impl, Ens.BusinessService)
. Now, you have turned your REST API into a Business Service.Webinar.API.Leaderboard.v1.impl
. Keep it disabled.getPlayerById
implementation:ClassMethod getPlayerById(playerId As %Integer) As %DynamicObject
{
set player = ##class(Webinar.Data.Player).%OpenId(playerId)
if '$isobject(player) {
do ..%SetStatusCode(404)
quit ""
}
// instantiate Business Service (interoperability framework)
set sc = ##class(Ens.Director).CreateBusinessService("Webinar.API.Leaderboard.v1.impl",.service)
$$$ThrowOnError(sc)
// build request message
set req = ##class(Ens.StringContainer).%New()
set req.StringValue = playerId
// send message to Business Operation
set sc = service.SendRequestSync("Webinar.BO.DummyREST", req, .rsp)
$$$ThrowOnError(sc)
// concatenate Business Operation response to REST API response
set player.Name = player.Name"("rsp.StringValue_")"
<span class="pl-k">do</span> <span class="pl-v">player</span>.<span class="pl-e">%JSONExportToStream</span>(.<span class="pl-e">stream</span>)
<span class="pl-k">quit</span> <span class="pl-v">stream</span>
}
Webinar.API.Leaderboard.v1.impl
Business Service in production configuration page.GET Player
request and check Message Viewer messages and visual trace.getPlayerById
to continue. Test again the GET Player
request in Postman and check it's OK.Now, you will build a basic scenario to manage the REST API in InterSystems API Manager (IAM).
Remember IAM can be managed using the UI or using the REST interface.
Tip: open a VS Code Terminal session and type the following so you can send curl
commands to IAM.
docker exec -it tools sh
curl -X POST --url http://iam:8001/services/ \
--data 'name=iris-leaderboard-v1-service' \
--data 'url=http://irisA:52773/leaderboard/api/v1' | jq
curl -X POST --url http://iam:8001/services/iris-leaderboard-v1-service/routes \
--data 'paths[]=/leaderboard' | jq
IAM - Get Player - No auth
request.key-auth
plugin in the service.curl -X POST http://iam:8001/services/iris-leaderboard-v1-service/plugins \
--data "name=key-auth" | jq
IAM - Get Player - No auth
request.systemA
curl -d "username=systemA&custom_id=SYSTEM_A" http://iam:8001/consumers/ | jq
curl -X POST http://iam:8001/consumers/systemA/key-auth -d 'key=systemAsecret' | jq
IAM - GET Player. Consumer SystemA
request.webapp
curl -d "username=webapp&custom_id=WEB_APP" http://iam:8001/consumers/ | jq
webapp
curl -X POST http://iam:8001/consumers/webapp/key-auth -d 'key=webappsecret' | jq
IAM - GET Players - Consumer WebApp
request./https://github.com/intersystems-ib/workshop-rest-iam/blob/master/shared/simulate.sh
webapp
consumer. Limit it to 100 requests in a minute.curl -X POST http://iam:8001/consumers/webapp/plugins \
--data "name=rate-limiting" \
--data "config.minute=100" | jq
Dev Portal > Settings
:Authentication Plugin=Basic
Auto Approve Access=Enable
Session Config (JSON)=Custom
and enter:{
"cookie_name": "portal_session",
"secret": "CHANGE_THIS",
"storage": "kong",
"cookie_secure": false
}
Dev Portal > Editor
New File +
and set File Type=spec
and File Path=leaderboard.yaml
.Sign Up
.Create API Credential
.IAM - Get Players - Developer
replacing the api-key
header by the actual credential you have just created.Documentation
.shared/audit.json
file.curl -X POST http://iam:8001/plugins/ \
--data "name=http-log" \
--data "config.http_endpoint=http://irisA:52773/audit/log" \
| jq
You will build a load balancing scenario between two IRIS instances with the leaderboard REST API.
This can be useful in case you want to spread the workload, blue-green deployment, etc.
Tip: open a VS Code Terminal session and type the following so you can send curl
commands to IAM.
docker exec -it tools sh
curl -s -X POST http://iam:8001/upstreams \
-d name=leaderboard-lb-stream \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-lb-stream/targets \
-d target=irisA:52773 \
-d weight=500 \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-lb-stream/targets \
-d target=irisB:52773 \
-d weight=500 \
| jq
curl -s -X POST http://iam:8001/services/ \
--data 'name=leaderboard-lb' \
--data 'host=leaderboard-lb-stream' \
--data 'path=/leaderboard/api/v1' \
| jq
curl -s -X POST http://iam:8001/services/leaderboard-lb/routes \
--data 'paths[]=/leaderboard-lb' \
| jq
IAM - GET Players - LB
request. Pay attention to the Node
property in the response body.You will now build a route by header scenario using three IRIS instances with the leaderboard REST API.
This could be useful in case you want use different servers depending on request headers (e.g. different versions).
Tip: open a VS Code Terminal session and type the following so you can send curl
commands to IAM.
docker exec -it tools sh
curl -s -X POST http://iam:8001/upstreams \
-d name=leaderboard-header-stream \
| jq
curl -s -X POST http://iam:8001/upstreams \
-d name=leaderboard-header-v1-stream \
| jq
curl -s -X POST http://iam:8001/upstreams \
-d name=leaderboard-header-v2-stream \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-header-stream/targets \
-d target=irisA:52773 \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-header-v1-stream/targets \
-d target=irisB:52773 \
| jq
curl -s -X POST http://iam:8001/upstreams/leaderboard-header-v2-stream/targets \
-d target=irisC:52773 \
| jq
curl -s -X POST http://iam:8001/services/ \
--data 'name=leaderboard-header' \
--data 'host=leaderboard-header-stream' \
--data 'path=/leaderboard/api/v1' \
| jq
curl -s -X POST http://iam:8001/services/leaderboard-header/routes \
--data 'paths[]=/leaderboard-header' \
| jq
route-by-header
plugin with some conditions on request header version
:curl -s -X POST http://iam:8001/services/leaderboard-header/plugins \
-H 'Content-Type: application/json' \
-d '{"name": "route-by-header", "config": {"rules":[{"condition": {"version":"v1"}, "upstream_name": "leaderboard-header-v1-stream"}, {"condition": {"version":"v2"}, "upstream_name": "leaderboard-header-v2-stream"}]}}' \
| jq
IAM - GET Players - Route By Header
using different version
header request values.Have a look at this example where you can see in action a REST API in IRIS as backend for an Angular application: https://github.com/intersystems-ib/iris-sample-rest-angular
IAM 2.3.3 added
Initial Release