Clear filter
Article
Yuri Marx · Aug 2, 2021
This article suggests to you some patterns to create REST API applications using IRIS.
Note: source code in https://github.com/yurimarx/movie
Class Pattern to the REST Application
To begin, see my suggestion for classes needed to create IRIS API applications:
IRISRESTApplication: CSP.REST class that will be the central controller for all REST requests and responses processed by the business services.
BusinessService: class with a business topic implementation. It can use one or more Persistent Domain Classes to persist and query data required by the business topic requirements.
Persistent Domain: persistent class to manage a SQL table.
Prereqs
VSCode;
Docker Desktop;
InterSystems ObjectScript Extension Pack.
Class Diagram to the Sample Application
I will create a Movie Catalog application to demonstrate the patterns suggested in the article:
Note: thanks to the https://openexchange.intersystems.com/package/iris-rest-api-template application. It was the base to this tutorial.
Setup the Sample Application
1. Create a folder movie in your file system. Open this folder in a new VSCode window.
2. Create the Dockerfile file inside movie folder to run IRIS Community edition into a Docker container instance. Content:
Docker file content
ARG IMAGE=intersystemsdc/iris-community:2020.3.0.221.0-zpm
ARG IMAGE=intersystemsdc/iris-community:2020.4.0.524.0-zpm
ARG IMAGE=intersystemsdc/iris-community
FROM $IMAGE
USER root
WORKDIR /opt/irisapp
RUN chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} /opt/irisapp
USER ${ISC_PACKAGE_MGRUSER}
COPY src src
COPY module.xml module.xml
COPY iris.script /tmp/iris.script
RUN iris start IRIS \
&& iris session IRIS < /tmp/iris.script \
&& iris stop IRIS quietly
3. Create the docker-compose.yml file inside movie folder to allows you run your docker instance and other instances together (not in this sample, but it is a good practice run from docker-compose instead dockerfile. Content:
Docker composer content
version: '3.6'
services:
iris:
build:
context: .
dockerfile: Dockerfile
restart: always
ports:
- 51773
- 1972:1972
- 52773:52773
- 53773
volumes:
- ./:/irisdev/app
4. Create the iris.script file inside movie folder to do some actions before run IRIS. This file is important to do custom terminal actions necessary for the application, like disable password expiration. Content:
iris.script content
;do $System.OBJ.LoadDir("/opt/irisapp/src","ck",,1)
zn "%SYS"
Do ##class(Security.Users).UnExpireUserPasswords("*")
zn "USER"
zpm "load /opt/irisapp/ -v":1:1
halt
5. Create the module.xml file inside movie folder to install and run your application using ZPM. This file is important to do the application endpoint configuration and install swagger-ui (web app used to run and test your API using swagger file). Content:
Module.xml content
<?xml version="1.0" encoding="UTF-8"?>
<Export generator="Cache" version="25">
<Document name="movie.ZPM">
<Module>
<Name>movie</Name>
<Version>1.0.0</Version>
<Packaging>module</Packaging>
<SourcesRoot>src</SourcesRoot>
<Resource Name="dc.movie.PKG"/>
<Dependencies>
<ModuleReference>
<Name>swagger-ui</Name>
<Version>1.*.*</Version>
</ModuleReference>
</Dependencies>
<CSPApplication
Url="/movie-api"
DispatchClass="dc.movie.MovieRESTApp"
MatchRoles=":{$dbrole}"
PasswordAuthEnabled="1"
UnauthenticatedEnabled="0"
Recurse="1"
UseCookies="2"
CookiePath="/movie-api"
/>
</Module>
</Document>
</Export>
You can see CSPApplication tag, used to run the application API in the /movie-api URI and enable or disable password to consume the API.
6. Create the LICENSE file inside movie folder to setting the license of your application. Content:
LICENSE content
MIT License
Copyright (c) 2019 InterSystems Developer Community Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7. Create the README.md file inside movie folder to document your application to the users using markdown language. Content:
## movie-rest-application
This is a sample of a REST API application built with ObjectScript in InterSystems IRIS.
8. Create .vscode folder inside movie folder. Create settings.json file inside .vscode folder to configure server connection between VSCode and your IRIS instance. Content:
Settings content
{
"files.associations": {
"Dockerfile*": "dockerfile",
"iris.script": "objectscript"
},
"objectscript.conn" :{
"ns": "USER",
"username": "_SYSTEM",
"password": "SYS",
"docker-compose": {
"service": "iris",
"internalPort": 52773
},
"active": true
},
"sqltools.connections": [
{
"namespace": "USER",
"connectionMethod": "Server and Port",
"showSystem": false,
"previewLimit": 50,
"server": "localhost",
"port": 32770,
"askForPassword": false,
"driver": "InterSystems IRIS",
"name": "objectscript-docker",
"username": "_SYSTEM",
"password": "SYS"
}
]
}
9. Create the folder src inside movie folder to put your source code folders and files.
10. Create dc folder inside src folder. This is a convention when your build projects to the InterSystems Developer Community, otherwise is not necessary.
11. Create movie folder inside dc folder. This folder will be the folder to your objectscript classes.
12. Create our first class, MovieRESTApp.cls file, inside src\dc\movie folder. This file will be the IRISRESTApplication class. Content:
MovieRESTApp content
Class dc.movie.MovieRESTApp Extends %CSP.REST
{
Parameter CHARSET = "utf-8";
Parameter CONVERTINPUTSTREAM = 1;
Parameter CONTENTTYPE = "application/json";
Parameter Version = "1.0.0";
Parameter HandleCorsRequest = 1;
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<!-- Server Info -->
<Route Url="/" Method="GET" Call="GetInfo" Cors="true"/>
<!-- Swagger specs -->
<Route Url="/_spec" Method="GET" Call="SwaggerSpec" />
</Routes>
}
ClassMethod %ProcessResult(pStatus As %Status = {$$$OK}, pResult As %DynamicObject = "") As %Status [ Internal ]
{
#dim %response As %CSP.Response
SET tSC = $$$OK
IF $$$ISERR(pStatus) {
SET %response.Status = 500
SET tSC = ..StatusToJSON(pStatus, .tJSON)
IF $isobject(tJSON) {
SET pResult = tJSON
} ELSE {
SET pResult = { "errors": [ { "error": "Unknown error parsing status code" } ] }
}
}
ELSEIF pStatus=1 {
IF '$isobject(pResult){
SET pResult = {
}
}
}
ELSE {
SET %response.Status = pStatus
SET error = $PIECE(pStatus, " ", 2, *)
SET pResult = {
"error": (error)
}
}
IF pResult.%Extends("%Library.DynamicAbstractObject") {
WRITE pResult.%ToJSON()
}
ELSEIF pResult.%Extends("%JSON.Adaptor") {
DO pResult.%JSONExport()
}
ELSEIF pResult.%Extends("%Stream.Object") {
DO pResult.OutputToDevice()
}
QUIT tSC
}
ClassMethod SwaggerSpec() As %Status
{
Set tSC = ##class(%REST.API).GetWebRESTApplication($NAMESPACE, %request.Application, .swagger)
Do swagger.info.%Remove("x-ISC_Namespace")
Set swagger.basePath = "/movie-api"
Set swagger.info.title = "Movie API"
Set swagger.info.version = "1.0"
Set swagger.host = "localhost:52773"
Return ..%ProcessResult($$$OK, swagger)
}
}
Note 1: The class extends CSP.REST to be used as the REST Endpoint.
Note 2: the parameter chaset is used to encode requests and responses with UTF-8.
Note 3: the CONVERTINPUTSTREAM is used to force the request content in the UTF-8, without this you can have problems with special latin chars.
Note 4: CONTENTTYPE is used to declare the content using JSON, not XML.
Note 5: HandleCorsRequest = 1 is necessary to allows you consume the API from other servers different from the IRIS server.
Note 6: Routes are used to declare API URI to each class method.
Note 7: SwaggerSpec from CSP.REST class allows you generate the API swagger (API web documentation) content.
Now you have the following folders and files:
13. Open VSCode Terminal (menu Terminal > New Terminal) and type:
docker-compose up -d --build
This will build the docker instance and run it.
14. Test your API with Swagger-UI. In the browser and type: http://localhost:52773/swagger-ui/index.html. Pay attention to the address bar (fix the url, if necessary to correct address)
Connection beetween VSCode and IRIS
1. Click in the ObjectScript bar (in the VSCode footer)
2. Select Toogle Connection in the Top:
3. Check the connection status into ObjectScript Explorer (you will be able to see folders and classes created):
Persistent Classes to the Movie Catalog Application
In this section we will create the persistent domain classes to store and query the business data. See the DBeaver Diagram:
1. Create the folder model inside src\dc\movie folder.
2. Create the Actor.cls file inside model folder. Write the content:
Class dc.movie.model.Actor Extends (%Persistent, %JSON.Adaptor)
{
Parameter %JSONREFERENCE = "ID";
Property actorId As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}}, SqlComputed ];
Property name As %VarString(MAXLEN = 120);
Property dob As %Date;
Property genre As %Integer(VALUELIST = ",1,2");
}
3. Create the Movie.cls file inside model folder. Write the content:
Class dc.movie.model.Movie Extends (%Persistent, %JSON.Adaptor)
{
Parameter %JSONREFERENCE = "ID";
Property movieId As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}}, SqlComputed ];
Property name As %VarString(MAXLEN = 120);
Property releaseDate As %Date;
Property duration As %Integer;
Property imdb As %String(MAXLEN = 300);
Property movieCategory As dc.movie.model.MovieCategory;
ForeignKey MovieCategoryFK(movieCategory) References dc.movie.model.MovieCategory();
}
4. Create the MovieCategory.cls file inside model folder. Write the content:
Class dc.movie.model.MovieCategory Extends (%Persistent, %JSON.Adaptor)
{
Parameter %JSONREFERENCE = "ID";
Property movieCategoryId As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}}, SqlComputed ];
Property name As %VarString(MAXLEN = 120);
}
5. Create the Casting.cls file inside model folder. Write the content:
Class dc.movie.model.Casting Extends (%Persistent, %JSON.Adaptor)
{
Parameter %JSONREFERENCE = "ID";
Property castingId As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}}, SqlComputed ];
Property movie As dc.movie.model.Movie;
ForeignKey MovieFK(movie) References dc.movie.model.Movie();
Property actor As dc.movie.model.Actor;
ForeignKey ActorFK(actor) References dc.movie.model.Actor();
Property characterName As %String(MAXLEN = 100);
Index CastingIndex On (movie, actor) [ Unique ];
}
See the created files:
Note 1: Parameter %JSONREFERENCE = "ID" allows return ID value inside JSON response.
Note 2: Property actorId As %Integer [ Calculated, SqlComputeCode = { set {*}={%%ID}}, SqlComputed ] and the other similar properties are used to return class+id into JSON response.
Note 3: (VALUELIST = "1,2") set possible values to 1 or 2 only.
Note 4: ForeignKey MovieFK(movie) References dc.movie.model.Movie() and similar are used to create a SQL foreign key reference.
Note 5: Index CastingIndex On (movie, actor) [ Unique ] and similar are used to not allows duplicate values combining properties in the On (movie and actor).
Note 6: I'm using Camel Case to property names because a best practice for JSON attribute names.
Business Service Classes to the Movie Catalog Application
In this section we will create the classes with business logic (methods to do persistence, query and calculations).
1. Create the service folder inside src\dc\movie.
2. Create CrudUtilService.cls file inside service folder. Write the content:
CrudUtilService content
Class dc.movie.service.CrudUtilService Extends %CSP.REST
{
Parameter CHARSET = "utf-8";
Parameter CONVERTINPUTSTREAM = 1;
Parameter CONTENTTYPE = "application/json";
Parameter Version = "1.0.0";
Parameter HandleCorsRequest = 1;
/// Return all the records
ClassMethod GetAll(DomainClass As %Persistent) As %Status
{
#dim tSC As %Status = $$$OK
Set rset = DomainClass.ExtentFunc()
Set %response.ContentType = ..#CONTENTTYPEJSON
Set %response.Headers("Access-Control-Allow-Origin")="*"
Write "["
if rset.%Next() {
Set actor = DomainClass.%OpenId(rset.ID)
Do actor.%JSONExport()
}
While rset.%Next() {
Write ","
Set actor = DomainClass.%OpenId(rset.ID)
Do actor.%JSONExport()
}
Write "]"
Quit tSC
}
/// Return one record
ClassMethod GetOne(DomainClass As %Persistent, id As %Integer) As %Status
{
#dim tSC As %Status = $$$OK
#dim e As %Exception.AbstractException
#; Set the response header to plain text
Set %response.ContentType = ..#CONTENTTYPEJSON
Set %response.Headers("Access-Control-Allow-Origin")="*"
Set domain = DomainClass.%OpenId(id)
If '$IsObject(domain) Quit ..Http404()
Do domain.%JSONExport()
Quit tSC
}
/// Creates a new record
ClassMethod Create(DomainClass As %Persistent) As %Status
{
#dim tSC As %Status = $$$OK
#dim e As %Exception.AbstractException
Set domain = DomainClass.%New()
Set data = {}.%FromJSON(%request.Content)
$$$TOE(tSC, domain.%JSONImport(data))
$$$TOE(tSC, domain.%Save())
Write domain.%JSONExport()
Set %response.Status = 204
Set %response.ContentType = ..#CONTENTTYPEJSON
Set %response.Headers("Access-Control-Allow-Origin")="*"
Quit tSC
}
/// Update a record with id
ClassMethod Update(DomainClass As %Persistent, id As %Integer) As %Status
{
#dim tSC As %Status = $$$OK
#dim e As %Exception.AbstractException
Set domain = DomainClass.%OpenId(id)
If '$IsObject(domain) Return ..Http404()
Set data = {}.%FromJSON(%request.Content)
$$$TOE(tSC, domain.%JSONImport(data))
$$$TOE(tSC, domain.%Save())
Write domain.%JSONExport()
Set %response.Status = 200
Set %response.ContentType = ..#CONTENTTYPEJSON
Set %response.Headers("Access-Control-Allow-Origin")="*"
Quit tSC
}
/// Delete a record with id
ClassMethod Delete(DomainClass As %Persistent, id As %Integer) As %Status
{
#dim tSC As %Status = $$$OK
#dim e As %Exception.AbstractException
Set domain = DomainClass.%OpenId(id)
If '$IsObject(domain) Return ..Http404()
$$$TOE(tSC, domain.%DeleteId(id))
Set %response.Status = 200
Set %response.ContentType = ..#CONTENTTYPEJSON
Set %response.Headers("Access-Control-Allow-Origin")="*"
Quit tSC
}
}
3. Create MovieService.cls file inside service folder. Write the content:
MovieService content
Class dc.movie.service.MovieService Extends %CSP.REST
{
ClassMethod GetAll() As %Status
{
Return ##class(dc.movie.service.CrudUtilService).GetAll(##class(dc.movie.model.Movie).%New())
}
ClassMethod GetOne(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).GetOne(##class(dc.movie.model.Movie).%New(), id)
}
ClassMethod Create() As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Create(##class(dc.movie.model.Movie).%New())
}
ClassMethod Update(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Update(##class(dc.movie.model.Movie).%New(), id)
}
ClassMethod Delete(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Delete(##class(dc.movie.model.Movie).%New(), id)
}
/// Return casting from the movie
ClassMethod GetMovieCasting(id As %Integer) As %Status
{
#dim tSC As %Status = $$$OK
Set qry = "SELECT actor->name AS actorName, characterName, movie->name AS movieName FROM dc_movie_model.Casting WHERE movie = ?"
Set tStatement = ##class(%SQL.Statement).%New()
Set qStatus = tStatement.%Prepare(qry)
If tSC'=1 {WRITE "%Prepare failed:" DO $System.Status.DisplayError(qStatus) QUIT}
Set rset = tStatement.%Execute(id)
Set %response.ContentType = ..#CONTENTTYPEJSON
Set %response.Headers("Access-Control-Allow-Origin")="*"
Set result = []
While rset.%Next() {
Set item = {}
Set item.actorName = rset.actorName
Set item.movieName = rset.movieName
Set item.characterName = rset.characterName
Do result.%Push(item)
}
Write result.%ToJSON()
Quit tSC
}
}
4. Create MovieCategoryService.cls file inside service folder. Write the content:
MovieCategoryService content
Class dc.movie.service.MovieCategoryService
{
ClassMethod GetAll() As %Status
{
Return ##class(dc.movie.service.CrudUtilService).GetAll(##class(dc.movie.model.MovieCategory).%New())
}
ClassMethod GetOne(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).GetOne(##class(dc.movie.model.MovieCategory).%New(), id)
}
ClassMethod Create() As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Create(##class(dc.movie.model.MovieCategory).%New())
}
ClassMethod Update(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Update(##class(dc.movie.model.MovieCategory).%New(), id)
}
ClassMethod Delete(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Delete(##class(dc.movie.model.MovieCategory).%New(), id)
}
}
5. Create ActorService.cls file inside service folder. Write the content:
ActorService content
Class dc.movie.service.ActorService
{
ClassMethod GetAll() As %Status
{
Return ##class(dc.movie.service.CrudUtilService).GetAll(##class(dc.movie.model.Actor).%New())
}
ClassMethod GetOne(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).GetOne(##class(dc.movie.model.Actor).%New(), id)
}
ClassMethod Create() As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Create(##class(dc.movie.model.Actor).%New())
}
ClassMethod Update(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Update(##class(dc.movie.model.Actor).%New(), id)
}
ClassMethod Delete(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Delete(##class(dc.movie.model.Actor).%New(), id)
}
}
6. Create CastingService.cls file inside service folder. Write the content:
CastingService content
Class dc.movie.service.CastingService
{
ClassMethod GetAll() As %Status
{
Return ##class(dc.movie.service.CrudUtilService).GetAll(##class(dc.movie.model.Casting).%New())
}
ClassMethod GetOne(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).GetOne(##class(dc.movie.model.Casting).%New(), id)
}
ClassMethod Create() As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Create(##class(dc.movie.model.Casting).%New())
}
ClassMethod Update(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Update(##class(dc.movie.model.Casting).%New(), id)
}
ClassMethod Delete(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CrudUtilService).Delete(##class(dc.movie.model.Casting).%New(), id)
}
}
7. Update the file MovieRESTApp.cls to create paths to all new service class methods. Write the content:
MovieRESTApp updated content
Class dc.movie.MovieRESTApp Extends %CSP.REST
{
Parameter CHARSET = "utf-8";
Parameter CONVERTINPUTSTREAM = 1;
Parameter CONTENTTYPE = "application/json";
Parameter Version = "1.0.0";
Parameter HandleCorsRequest = 1;
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<!-- Server Info -->
<Route Url="/" Method="GET" Call="GetInfo" Cors="true"/>
<!-- Swagger specs -->
<Route Url="/_spec" Method="GET" Call="SwaggerSpec" />
<!-- List all movies -->
<Route Url="/movies" Method="GET" Call="GetAllMovies" />
<!-- Get a movie -->
<Route Url="/movies/:id" Method="GET" Call="GetMovie" />
<!-- Get the movie casting -->
<Route Url="/movies/casting/:id" Method="GET" Call="GetMovieCasting" />
<!-- Create new movie -->
<Route Url="/movies" Method="POST" Call="CreateMovie" />
<!-- Update a movie -->
<Route Url="/movies/:id" Method="PUT" Call="UpdateMovie" />
<!-- Delete a movie -->
<Route Url="/movies/:id" Method="DELETE" Call="DeleteMovie" />
<!-- List all movie categories -->
<Route Url="/categories" Method="GET" Call="GetAllMovieCategories" />
<!-- Get a movie category -->
<Route Url="/categories/:id" Method="GET" Call="GetMovieCategory" />
<!-- Create new movie category -->
<Route Url="/categories" Method="POST" Call="CreateMovieCategory" />
<!-- Update a movie category -->
<Route Url="/categories/:id" Method="PUT" Call="UpdateMovieCategory" />
<!-- Delete a movie category -->
<Route Url="/categories/:id" Method="DELETE" Call="DeleteMovieCategory" />
<!-- List all actors -->
<Route Url="/actors" Method="GET" Call="GetAllActors" />
<!-- Get a actor -->
<Route Url="/actors/:id" Method="GET" Call="GetActor" />
<!-- Create new actor -->
<Route Url="/actors" Method="POST" Call="CreateActor" />
<!-- Update a actor -->
<Route Url="/actors/:id" Method="PUT" Call="UpdateActor" />
<!-- Delete a actor -->
<Route Url="/actors/:id" Method="DELETE" Call="DeleteActor" />
<!-- List all castings -->
<Route Url="/castings" Method="GET" Call="GetAllCastings" />
<!-- Get a actor -->
<Route Url="/castings/:id" Method="GET" Call="GetCasting" />
<!-- Create new actor -->
<Route Url="/castings" Method="POST" Call="CreateCasting" />
<!-- Update a actor -->
<Route Url="/castings/:id" Method="PUT" Call="UpdateCasting" />
<!-- Delete a actor -->
<Route Url="/castings/:id" Method="DELETE" Call="DeleteCasting" />
</Routes>
}
/// List movies
ClassMethod GetAllMovies() As %Status
{
Return ##class(dc.movie.service.MovieService).GetAll()
}
/// Get movie casting
ClassMethod GetMovieCasting(id As %Integer) As %Status
{
Return ##class(dc.movie.service.MovieService).GetMovieCasting(id)
}
/// Get a movie
ClassMethod GetMovie(id As %Integer) As %Status
{
Return ##class(dc.movie.service.MovieService).GetOne(id)
}
// Create a new movie
ClassMethod CreateMovie() As %Status
{
Return ##class(dc.movie.service.MovieService).Create()
}
// Update a movie
ClassMethod UpdateMovie(id As %Integer) As %Status
{
Return ##class(dc.movie.service.MovieService).Update(id)
}
// Delete a movie
ClassMethod DeleteMovie(id As %Integer) As %Status
{
Return ##class(dc.movie.service.MovieService).Delete(id)
}
/// List movies categories
ClassMethod GetAllMovieCategories() As %Status
{
Return ##class(dc.movie.service.MovieCategoryService).GetAll()
}
/// Get a movie category
ClassMethod GetMovieCategory(id As %Integer) As %Status
{
Return ##class(dc.movie.service.MovieCategoryService).GetOne(id)
}
// Create a new movie category
ClassMethod CreateMovieCategory() As %Status
{
Return ##class(dc.movie.service.MovieCategoryService).Create()
}
// Update a movie category
ClassMethod UpdateMovieCategory(id As %Integer) As %Status
{
Return ##class(dc.movie.service.MovieCategoryService).Update(id)
}
// Delete a movie category
ClassMethod DeleteMovieCategory(id As %Integer) As %Status
{
Return ##class(dc.movie.service.MovieCategoryService).Delete(id)
}
/// List actors
ClassMethod GetAllActors() As %Status
{
Return ##class(dc.movie.service.TestActorService).GetAll()
}
/// Get an actor
ClassMethod GetActor(id As %Integer) As %Status
{
Return ##class(dc.movie.service.ActorService).GetOne(id)
}
// Create a new actor
ClassMethod CreateActor() As %Status
{
Return ##class(dc.movie.service.ActorService).Create()
}
// Update an actor
ClassMethod UpdateActor(id As %Integer) As %Status
{
Return ##class(dc.movie.service.ActorService).Update(id)
}
// Delete an actor
ClassMethod DeleteActor(id As %Integer) As %Status
{
Return ##class(dc.movie.service.ActorService).Delete(id)
}
/// List castings
ClassMethod GetAllCastings() As %Status
{
Return ##class(dc.movie.service.CastingService).GetAll()
}
/// Get a casting
ClassMethod GetCasting(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CastingService).GetOne(id)
}
// Create a new casting item
ClassMethod CreateCasting() As %Status
{
Return ##class(dc.movie.service.CastingService).Create()
}
// Update a casting
ClassMethod UpdateCasting(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CastingService).Update(id)
}
// Delete a casting
ClassMethod DeleteCasting(id As %Integer) As %Status
{
Return ##class(dc.movie.service.CastingService).Delete(id)
}
/// General information
ClassMethod GetInfo() As %Status
{
SET version = ..#Version
SET fmt=##class(%SYS.NLS.Format).%New("ptbw")
SET info = {
"Service": "Movie API",
"version": (version),
"Developer": "Yuri Gomes",
"Status": "Ok",
"Date": ($ZDATETIME($HOROLOG))
}
Set %response.ContentType = ..#CONTENTTYPEJSON
Set %response.Headers("Access-Control-Allow-Origin")="*"
Write info.%ToJSON()
Quit $$$OK
}
ClassMethod %ProcessResult(pStatus As %Status = {$$$OK}, pResult As %DynamicObject = "") As %Status [ Internal ]
{
#dim %response As %CSP.Response
SET tSC = $$$OK
IF $$$ISERR(pStatus) {
SET %response.Status = 500
SET tSC = ..StatusToJSON(pStatus, .tJSON)
IF $isobject(tJSON) {
SET pResult = tJSON
} ELSE {
SET pResult = { "errors": [ { "error": "Unknown error parsing status code" } ] }
}
}
ELSEIF pStatus=1 {
IF '$isobject(pResult){
SET pResult = {
}
}
}
ELSE {
SET %response.Status = pStatus
SET error = $PIECE(pStatus, " ", 2, *)
SET pResult = {
"error": (error)
}
}
IF pResult.%Extends("%Library.DynamicAbstractObject") {
WRITE pResult.%ToJSON()
}
ELSEIF pResult.%Extends("%JSON.Adaptor") {
DO pResult.%JSONExport()
}
ELSEIF pResult.%Extends("%Stream.Object") {
DO pResult.OutputToDevice()
}
QUIT tSC
}
ClassMethod SwaggerSpec() As %Status
{
Set tSC = ##class(%REST.API).GetWebRESTApplication($NAMESPACE, %request.Application, .swagger)
Do swagger.info.%Remove("x-ISC_Namespace")
Set swagger.basePath = "/movie-api"
Set swagger.info.title = "Movie API"
Set swagger.info.version = "1.0"
Set swagger.host = "localhost:52773"
Return ..%ProcessResult($$$OK, swagger)
}
}
8. The files and folders to the final project are:
9. Test your new methods acessing http://localhost:52773/swagger-ui/index.html.
Note 1: REST paths are following business topic in plural with /id when we need pass id to the entity and camel case to paths to.
Note 2: We use verb GET to queries, POST to new records, PUT to update record and DELETE to delete a record.
Note 3: In <Route Url="/movies/casting/:id" Method="GET" Call="GetMovieCasting" /> I used /casting indicating a second purpose (get the movie and it casting). This method runs ToJSON(), because is a DynamicArray ([]) with Dynamic items ({}).
Note 4: I created the CrudUtilService class utility to do generic CRUD methods, following the Dont Repeat Yourself principle.
Enjoy this tutorial!
Hi Yuri
You didn't use GoJS by any chance for the UML etc... If you look in the General Section of Discord I have written a chunk of stuff about GoJS and how a developer could build the necessary data collectors into their classes and then having but the data sets render the diagrams through GoJS Thanks for the tip! I did use StarUML, but in the next time I will consider GoJS Great article.
Some comments on formatting:
Move large code blocks under spoiler to improve readability using button.
There are a lot of empty lines in code blocks, they can be safely removed.
On the article itself my only issue is you create 5 methods per class:
GET /class
POST /class
GET /class/object
PUT /class/object
DELETE /class/object
That's a lot of code even for 4 classes, but what about a case where there are 10 classes? 50? 100? Generally I would recommend writing 5 methods which would process objects of any allowed class (see RESTForms2). Fixed and improved, thanks!!! Hi Yuri
I forgot to mention that I agree with Eduard that your article is excellent.
Nigel Thanks!!! Excellent work @Yuri.Gomes, lots of useful details for the reader! I like DRY too :-) Thanks @Luca.Ravazzolo
Announcement
Anastasia Dyubaylo · Sep 29, 2021
Hey Community,
Get ready for the next French Stream on InterSystems Technologies!
The new monthly event hosted by @Guillaume.Rongier7183 for French-speaking developers familiar with InterSystems technologies will be live stream October 7th at 12:00 Paris time. You will discover all the news and tips related to our solutions!
👉 Direct link to join: https://youtu.be/Rbp-u-3vY-8
On the Agenda:
🗞 News
https://marcusnoble.co.uk/2021-09-01-migrating-from-docker-to-podman/
https://community.intersystems.com/post/scrollable-resulset-pagination-sample
https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme
https://globalmasters.intersystems.com/challenges
https://community.intersystems.com/post/use-systemexternal-interface-python
💡 Did you know?
How to use a debugger on VSCode
🗂 Focus on
InterSystems Analytics demo
👨💻 Let's talk with
@Jacques.Huser, Technical Specialist at InterSystems
Don’t miss the French Stream #5 👉 https://youtu.be/Rbp-u-3vY-8!
Enjoy watching the prev streams on YouTube:
Stream #1
Stream #2
Stream #3
Stream #4
Stay tuned!
Question
Nagarjuna Podapati · Feb 10, 2022
Dear Comminuty,Can you provide learning path recommendations to start on "InterSystems IRIS for Health"? Please check this article of @Yuri.Gomes https://community.intersystems.com/post/learning-path-beginners Thanks Waseem!This content is good enough to start.. Hi @Nagarjuna.Podapati We're building out several learning programs based on roles that will better help in the future here. But the paths exist today! What are you trying to accomplish with IRIS for Health? Based on what you're trying to accomplish, we can recommend a personalized set of learning paths.
That said, if you're going to need to do most things or you're not sure yet, I recommend Getting Started with InterSystems (Health).
Announcement
Anastasia Dyubaylo · Aug 5, 2020
Hey Developers!
Want to participate again in the competition of creating open-source solutions using InterSystems IRIS Data Platform? Please welcome:
➡️ InterSystems IRIS for Health FHIR Contest ⬅️
Duration: August 10-30, 2020
Prizes
1. Experts Nomination - winners will be determined by a specially selected jury:
🥇 1st place - $2,000
🥈 2nd place - $1,000
🥉 3rd place - $500
2. Community Nomination - an application that will receive the most votes in total:
🥇 1st place - $1,000
🥈 2nd place - $500
If several participants score the same amount of votes they all are considered as winners and the money prize is shared among the winners.
Who can participate?
Any Developer Community member, except for InterSystems employees. Create an account!
Contest Period
August 10-23: Two weeks to upload your applications to Open Exchange (also during this period, you can edit your projects).
August 24-30: One week to vote.
August 31: Winners announcement.
The topic
💡 Fast Healthcare Interoperability Resources – FHIR 💡
Develop a FHIR solution using InterSystems IRIS for Health data platform.
We will choose the best FHIR application built with InterSystems IRIS for Health. Your application could be a library, package, tool, or any FHIR solution which uses InterSystems IRIS for Health.
The application should work on InterSystems IRIS for Health Community Edition.
The application should be Open Source and published on GitHub.
If the application satisfies some additional requirements listed in this post, it gets additional technology votes.
Template application
IRIS-FHIR-Template
Helpful resources
1. How to submit your app to the contest:
How to publish an application on Open Exchange
How to submit an application for the contest
2. Documentation:
FHIR Support in InterSystems Products
3. Online courses:
Learn FHIR for Software Developers
Building SMART on FHIR Apps with InterSystems FHIR Sandbox
Exploring FHIR Resource APIs
Using InterSystems IRIS for Health to Reduce Readmissions
Connecting Devices to InterSystems IRIS for Health
Monitoring Oxygen Saturation in Infants
FHIR Integration QuickStart
4. Videos:
6 Rapid FHIR Questions
SMART on FHIR: The Basics
Developing with FHIR - REST APIs
FHIR in InterSystems IRIS for Health
FHIR API Management
Searching for FHIR Resources in IRIS for Health
Also, please check the related FHIR playlist on DC YouTube.
5. Q&A on FHIR:
Explore FHIR tag on DC
Ask questions on community.fhir.org
Judgment
Please find the Judgment and Voting Rules for the Contest here.
So!
Ready. Set. Code.
Please join our exciting coding marathon!
❗️ Please check out the Official Contest Terms here.❗️
A very helpful repository to generate synthetic FHIR compatible data shared by @Phillip.Booth
E.g. to generate data for 5 patients in an /output subfolder of a current folder call:
docker run --rm -v $PWD/output:/output --name synthea-docker intersystemsdc/irisdemo-base-synthea:version-1.3.4 -p 5
Which you can load then into IRIS for Health with:
zw ##class(HS.FHIRServer.Tools.DataLoader).SubmitResourceFiles("path/output", "FHIRNamespace", "/restapi/endpoint/fhir/r4") Developers!
We invite you to suggest task ideas for the Health FHIR Contest and get points on Global Masters!
Submit your ideas here: https://github.com/intersystems-community/FHIR-contest-topics/issues and you will get:
800 points - if your task idea is approved
2,000 points more - if you publish an article on DC describing your task idea in details (description, possible solution approaches, links to the documentation, links to the code samples, sample data, etc.)
3,000 points more - if you publish a solution template to start solving the task!
Please let me know after your idea is published because the points are awarded manually. Developers!
You are welcome to use iris-fhir-template to build your InterSystems IRIS for Health solutions for the contest.
The template uses the preview release 2020.3 of InterSystems IRIS for Health Developers!
Are you ready to participate in our exciting contest? 🚀
The contest begins! And we're waiting for your cool projects! Hi guys,
We introduced several topics you may find interesting and use to build FHIR applications with InterSystems IRIS for Health for the FHIR contest.
Amongst them are:
a frontend mobile or web app for FHIR server FHIR REST API or SQL;
Backend apps to perform complex queries and maintain rules;
work with different HL7 data standards.
More details in this post.
Stay tuned! Hi all!
Here're some interesting task ideas for the FHIR contest from our GM Advocates:
@Yuri.Gomes: App to maintain electronic immunization history using FHIR to get and persist data.
@Rasha.Sadiq: Creating a Test to dynamically identify the severity of an episode by taking the diagnosis and age into consideration.
@Udo.Leimberger3230: HL7 v2, HL7v3 Gateway with analyse options (I am thinking about a QueueManagement like RabbitMQ but for the H7 protocol -- Module for ManagementPortal - resend Messages / Cancel Messages ... and ... and .. and.. ).
So!
You can also suggest your own ideas and get up to 5,800 points on Global Masters.
Whose idea will be next? 🔥 Want more?
Watch the latest video on InterSystems Developers YouTube related to the FHIR contest:
⏯ InterSystems IRIS for Health FHIR Contest Kick-Off Webinar
This video describes how to use the IRIS-FHIR-Template to build FHIR solutions using InterSystems IRIS for Health. This template will help you to get started with the contest.
Stay tuned! ✌🏼 Another idea for the contest: use your iPhone's health app data to transform into FHIR data and submit to the FHIR server. See the details Developers!
Here're some additional helpful resources that will help you take part in our competition or even win the IRIS contest! Check them out:
HL7 QuickStart
InterSystems IRIS 2020.1 Tech Talk: API-First Development
InterSystems IRIS 2020.1 Tech Talk: Using InterSystems Managed FHIR Service in the AWS Cloud
FHIR - 7 Most Important Facts in 7 Minutes
Happy coding! 👨💻 So guys,
We introduced some technology bonuses that will give you extra points in the voting:
FHIR Server REST API usage
The usage of SQL Schema of FHIR Resources
Healthcare standards data transformation
Docker container usage
Full details in this post. Ok! After the first week of registration, we already have 2 applications participating in the FHIR competition! Please welcome:
➡️ iris4health-fhir-analytics project by @José.Pereira
➡️ QR-FHIR project by @Yuri.Gomes
So! Who'll be the next? How to apply for the programming contest
Log in to Open Exchange, open your applications section.
Open the application which you want to apply for the contest and click Apply for Contest.
Make sure the status is 'Published'.
The application will go for the review and if it fits the topic of the contest the application will be listed on the contest board. Developers!
You have 6 days to submit your application for the FHIR contest!
Feel free to submit if you haven't finished your project – you'll be able to fix bugs and make improvements during the voting week too. Hey Developers,
One more application is already in the game: iris-fhir-portal project by @Henrique
Give it a try! 🔥 Hey Community,
Only 5 days left to upload your apps to the FHIR Contest!
Show your best coding skills on InterSystems IRIS for Health, earn some $$$ and glory! 💥 Hey guys,
You're very welcome to join the InterSystems Developers Discord Channel to discuss all topics and questions related to the IRIS Programming Contests. There are lively discussions with InterSystems developers!
Join us! 😎 Voting for the best application will begin soon!
Only 3 days left before the end of registration for the FHIR Programming Contest.
Don't miss your chance to win! 🏆
Announcement
Anastasia Dyubaylo · Nov 16, 2020
Hey Developers,
This week is a voting week for the InterSystems Interoperability Contest! So, it's time to give your vote to the best solutions built with InterSystems IRIS.
🔥 You decide: VOTING IS HERE 🔥
How to vote? This is easy: you will have one vote, and your vote goes either in Experts Nomination or in Community Nomination.
Experts Nomination
If you are InterSystems Product Manager, or DC moderator, or Global Master from Specialist level and above cast your vote in the Expert nomination.
Community Nomination
If you ever contributed to DC (posts or replies) and this was not considered as spam you are able to vote for the applications in the Community nomination.
Voting
Voting takes place on the Open Exchange Contest Page and you need to sign in to Open Exchange – you can do it with your DC account credentials.
If you changed your mind, cancel the choice and give your vote to another application – you have 7 days to choose!
Contest participants are allowed to fix the bugs and make improvements to their applications during the voting week, so don't miss and subscribe to application releases!
➡️ Also, please check out the Judgment and Voting Rules for the Contest here. Hey Developers,
After the first day of the voting we have:
Expert Nomination, Top 3
Open API Client Gen – 13
IRIS Interoperability Message Viewer – 7
OCR Service – 4
➡️ The leaderboard.
Community Nomination, Top 3
Open API Client Gen – 9
IRIS Interoperability Message Viewer – 4
interoperability-integratedml-adapter – 3
➡️ The leaderboard.
Experts, we are waiting for your votes! 🔥
Participants, improve & promote your solutions! Here are the results after 2 days of voting:
Expert Nomination, Top 3
Open API Client Gen – 13
IRIS Interoperability Message Viewer – 7
OCR Service – 7
➡️ The leaderboard.
Community Nomination, Top 3
OCR Service – 13
Open API Client Gen – 10
IRIS Interoperability Message Viewer – 8
➡️ The leaderboard.
So, the voting continues.
Please support the application you like! Voting for the InterSystems Interoperability Contest goes ahead!
And here're the results at the moment:
Expert Nomination, Top 3
Open API Client Gen – 16
IRIS Interoperability Message Viewer – 7
OCR Service – 7
➡️ The leaderboard.
Community Nomination, Top 3
Open API Client Gen – 14
IRIS Interoperability Message Viewer – 10
OCR Service – 8
➡️ The leaderboard. Hey Developers,
Technology bonuses for the contest apps have been published.
Check them out and cast your vote for the application you like! ✅ Developers! Only 2 days left before the end of voting.
Please check out the Contest Board and vote for the solutions you like! 👍🏼 Last day of voting! ⌛
Please check out the Contest Board.Our contestants need your votes! 📢
Announcement
Anastasia Dyubaylo · Jan 20, 2021
Hey Developers,
Thank you so much for being with InterSystems Developer Community for yet another year!
More than 8K+ users are already registered in our community! And day by day our team is trying to make it better and more useful for InterSystems developers.
We want to know how helpful the Developer Community is for you today. Please go through this short survey which will let us know what do you think and what could be improved!
👉🏼 Developer Community Survey 2020 👈🏼
Note: The survey will take less than 5 minutes to complete.
Please take part in our survey. Any of your feedback is also welcome in the comments on this post!
Enjoy! 😉 Great Hi Developers,
Please tell us what you think about Developer Community! Your thoughts and ideas are very important for us. Hey guys,
Only a few days left! Please take a quick survey and help us become better:
👉🏼 Developer Community Survey 2020 👈🏼
Announcement
Anastasia Dyubaylo · Jun 19, 2021
Hey Developers,
We're pleased to announce the next InterSystems online programming competition:
🏆 InterSystems AI Programming Contest 🏆
Duration: June 28 - July 25, 2021
Total prize: $8,750
Landing page: https://contest.intersystems.com
Prizes
1. Experts Nomination - a specially selected jury will determine winners:
🥇 1st place - $4,000
🥈 2nd place - $2,000
🥉 3rd place - $1,000
2. Community winners - applications that will receive the most votes in total:
🥇 1st place - $1,000
🥈 2nd place - $500
🥉 3rd place - $250
If several participants score the same amount of votes, they all are considered winners, and the money prize is shared among the winners.
Who can participate?
Any Developer Community member, except for InterSystems employees. Create an account!
👥 Developers can team up to create a collaborative application. Allowed from 2 to 5 developers in one team.
Do not forget to highlight your team members in the README of your application – DC user profiles.
Contest Period
🛠 June 28 - July 18: Application development and registration phase.
✅ July 19 - July 25: Voting period.
🎉 July 26: Winners announcement.
Note: Developers can improve their apps throughout the entire registration and voting period.
The topic
🤖 Artificial Intelligence and Machine Learning 🤖
Develop an AI/ML solution using InterSystems IRIS. Your application could be a library, package, tool, or any AI/ML solution which uses InterSystems IRIS.
Here are the requirements:
Accepted applications: new to Open Exchange apps or existing ones, but with a significant improvement. Our team will review all applications before approving them for the contest.
Build the app that either uses AI/ML capabilities with InterSystems IRIS.
The application should work either on IRIS Community Edition or IRIS for Health Community Edition or IRIS Advanced Analytics Community Edition.
The application should be Open Source and published on GitHub.
The README file to the application should be in English, contain the installation steps, and contain either the video demo or/and a description of how the application works.
Source code of the InterSystems ObjectScript part (if any)should be available in UDL format (not XML). Example.
The requirements above are subject to change.
➡️ Some ideas for contestants.
Use Embedded Python to join the current contest!
Embedded Python is a new feature of InterSystems IRIS that gives you the option to use python as a "first-class citizen" in backend business logic development with InterSystems classes.
Embedded Python could be used in "on-demand" images that could be delivered via InterSystems InterSystems Early Access Program (EAP) if you refer to python-interest@intersystems.com. More details here.
Helpful resources
1. Templates we suggest to start from:
InterSystems IntegratedML template
IRIS R Gateway template
2. Data import tools:
Data Import Wizard
CSVGEN - CSV import util
CSVGEN-UI - the web UI for CSVGEN
3. Documentation:
Using IntegratedML
4. Online courses & videos:
Learn IntegratedML in InterSystems IRIS
Preparing Your Data for Machine Learning
Predictive Modeling with the Machine Learning Toolkit
IntegratedML Resource Guide
Getting Started with IntegratedML
Machine Learning with IntegratedML & Data Robot
5. How to submit your app to the contest:
How to publish an application on Open Exchange
How to apply for the contest
Judgment
Voting rules will be announced soon. Stay tuned!
So!
We're waiting for YOUR great project – join our coding marathon to win!
❗️ Please check out the Official Contest Terms here.❗️
Here are some ideas for contestants:
New ML language. Interoperability with numerical computational languages or even CASes proper are great and offer the freedom of choice. Furthermore, these math-oriented languages allow faster problem search/space traversal than more generalized languages such as Python. Several classes of supporting ML problems can be solved with them. Callout interface makes implementation process easy (reference community implementations: PythonGateway, RGateway, JuliaGateway). Suggested languages: Octave, Scilab.
New showcases in IoT, Real-Time predictions, RPA. Convergent Analytics group provides a lot of starting templates in these fields - as InterSystems IRIS capabilities are an especially good fit for them. I'm always interested in more examples, especially real-life examples of machine learning.
Data Deduplication solutions. Do you have a dataset with a lot of dirty data and know how to clean it? Great. Make a showcase out of it.
Reinforcement learning showcases. Examples of Partially observable Markov decision process or other reinforcement learning technologies.
Hey Developers!
The competition starts on Monday and we have prepared Helpful resources for you:
InterSystems IntegratedML template
IRIS R Gateway template
Feel free to check it out! Developers!The InterSystems AI Programming Contest is open.The first week of registration has begun. Make the most of the tips and materials to create the best appl!😉
We'll wait for you on our Contest board. Hey developers,
If you haven't seen it yet, don't miss our official contest landing page: https://contest.intersystems.com/ 🔥
Stay tuned! Hey Developers,
The recording of the InterSystems AI Contest Kick-Off Webinar is available on InterSystems Developers YouTube! Please welcome:
⏯ InterSystems AI Contest Kick-Off Webinar
Thanks to our speakers! 🤗 Developers!
We are waiting for your great apps!
Don't forget to participate! Hey Devs!
Two weeks before the end of registration!⏳Upload your cool apps and register for the competition!🚀
Here some information how to submit your app to the contest:
How to publish an application on Open Exchange
How to apply for the contest
Hey Developers,
💥 Use Embedded Python to join the current contest!
Embedded Python is a new feature of InterSystems IRIS that gives you the option to use python as a "first-class citizen" in backend business logic development with InterSystems classes.
⏯ Short demo of Embedded Python by @Robert.Kuszewski
Embedded Python could be used in "on-demand" images that could be delivered via InterSystems Early Access Program (EAP) if you refer to python-interest@intersystems.com. More details here.
⬇️ Template package on how to use Embedded Python deployable with ZPM. Don't forget to change the image to the one you get from the EAP.
Stay tuned! Developers!
We are waiting for your solutions!
Please register on our Landing page Future Participants!
Don't forget about our special technology bonuses that will give you extra points in the voting. 🤩
Try them and start to upload your cool apps! 😃
Happy coding!
Dear Developers!
We have one additional idea for an application.Often, new UI components and pages are relevant to provide customers additional information within an app. We would like inject "AI Apps" into our HealthShare Clinical Viewer (HS CV).
A custom app wrapper component in the HS CV needs to be implemented. We will provide a HealthShare environment together with an AI App on the AI platform of DataRobot to you. https://www.datarobot.com/blog/introducing-no-code-ai-app-builder/
For additional details please reach out to Eduard or myself. We are ready to chat! Dear Developers,
The last week of the registration has begun!
If you still don't have ideas for your app, you can choose some preparedby @Eduard.Lebedyuk https://community.intersystems.com/post/intersystems-ai-programming-contest#comment-159576
and @Thomas.Nitzsche https://community.intersystems.com/post/intersystems-ai-programming-contest#comment-160796
We'll wait for your solutions on our Contest board. 😃
Hey Developers!
We remind you that you can register with one click 😉, just press the button "I want to participate" on our
Landing page: https://contest.intersystems.com
Who will be the first? 🤗 Hey Developers,
The first application is already on the Contest Board!
ESKLP by @Aleksandr.Kalinin6636
Who's next? Hey Devs!
Only 2 days left till the end of the registration. ⏳
Hurry up to upload your cool apps to our Contest Board!
Announcement
Marco Alemagna · Jul 28, 2021
An international Company ,whose Headquarter is based in Switzerland, is looking for a Senior InterSystems Developer who will join the new IT center based in Turin.
The Senior developers will design, develop and help the operation of Event Processing Solution based on the IRIS Data Platform. He will work with the global teams (India, USA and Europe) and with external partners/vendors. He/she works in coordination with the in-place Community of Practices to ensure the coherence of the development with the program architecture. The Senior Developer will need to deliver several work packages with local/offshore resources. He/She will play a key role during the release process in coordination with both the development team and the Application operational team.
QUALIFICATION AND EXPERIENCE
Required:
-At least 5 years' experience with InterSystems Technology (IRIS/CACHE) with experience in the following subjects:
- COS (cache object script) & OO (object oriented) development using classes
-SQL (both embedded and dynamic), class queries & SP (stored procedures)
-Proven experience with Interoperability (aka Ensamble) in both design and implement solution in a complex production, experience with use of varius adaptators ( e.g. SQL, REST, HTTP)
If you're interested in receiving more information about the company and the position, please fill free to send your curriculum at the following email address:
marco.alemagna@ellipsis-partners.com
Announcement
Derek Robinson · Jul 27, 2021
Episode 20 of Data Points features a conversation with Bob Kuszewski about the Kubernetes and the InterSystems Kubernetes Operator (IKO). Take a listen to hear about some of the use cases and features of IKO, plus how it compares to InterSystems Cloud Manager (ICM) within the cloud space.
For all episodes of Data Points and to subscribe on your favorite app, check out https://datapoints.intersystems.com.
Article
Jose-Tomas Salvador · Nov 9, 2021
For some years I missed being able to offer, to everybody interested in ObjectScript, a tutorial more or less complete, to start with ObjectScript. Something that could help more and make things easier to those new developers that come to our technology... something intermediate, halfway between the common "Hello World!", that doesn't really get you further, and the "Advanced Training", that is unaffordable because of lack of time,etc.
If there were something truly helpful not only as an introduction to the ecosystem, but as a starting point, as a boost, to really start to walk into ObjectScript and move forward by yourself... wouldn't that be awesome?
In short... as I say, the idea that something like that should exist, it's going around me for a while... and then, the pandemic... and we had to stay at home, almost no holidays, and loooonnggg weekends,... what a better situation to do what we always say we would do if we had time?
So..no sooner said than done... I started to work on it several months ago and spend some free time here and there to develop this idea... you will tell me how it is (be kind), because:
Today I can announce officially the birth of ¡¡¡1st Video Tutorial in Spanish of ObjectScript 101++ !!!
Ok, ok,... plus, plus, .... I didn't date to call it 102... or intermediate or so....... It's composed of 6 videos plus one with a short introduction...
Wait... yes... I know... I said Spanish... and this is community in english...I know... It's uploaded in the ES community since September...but last week I published the last video there and I thought that it could also be useful to english native speakers that understand (or are learning) spanish... that's why I decided to post it also here. Also, I'm dealing with subtitles in YouTube... so to include the english translation. I'll link the videos for which I consider the english translation is fine as soon as they're ready. Right now, only the first 3 are reviewed (I will need some more weekends ).
Code samples and exercises used in different parts of the tutorial are in GitHub. To see the videos better, my advice is to do it in full size screen... they are also available in YouTube.
IMPORTANT: It's not an InterSystems official training and doesn't intend to (it can't in any way) replace the formal classroom training, which are more extense, rigurous and complete. My intention is for this tutorial to be an additional resource to introduce you into the ObjectScript world as a starting point and a spur, something that helps you to move forward by yourself, together with other resources from eLearning, formal training, documentation or this community!
¡¡Enjoy!!
Chapter 0 - Introduction
Chapter 1 - Settling down
Chapter 2 - Iterating
Chapter 3 - My Execution Context
Chapter 4 - Indirection and Globals
Chapter 5 - Objects in ObjectScript
Chapter 6 - What about SQL?
@Jose-Tomas.Salvador - thank you for the effort you put into creating this and making it available to the Community :) Ok, I've just reviewed the translation of Chapter 4 to english. Here I talk briefly about Indirection in ObjectScript and, mostly, about Globals... Enjoy! Well, this took some more time.... but arrived on time before end of year. Chapter 5 captions reviewed!! In this chapter I talk about OO.OO in ObjectScript, a bit of JSON native support, Macros, Error handling,... with guided hands-on.
Just one left. Enjoy! It was funny really... and served me to realize how hard is to create this kind of material. Now I appreciate it much more. :-) Well, it has been a long trip... but Chapter 6, "What's about SQL?" is finally published with reviewed English CC...
In this last chapter I'll show you what's about the Multi-Model architecture of InterSystems IRIS, and how ObjectScript is able to work also with SQL paradigm in a very easy way to also cover that feature of the platform.We'll do hands-on exercises of embedded and dynamic SQL using the small class model we defined in the previous chapter... which now we'll see as an E/R model.
And... That's all folks!
Happy Coding! worth watching content, thanks! Hi again, I've just added to the article the slides that I used during the video tutorial. Feel free to use all of some of them. If you do, the only thing I would kindly ask you is to include a link as a reference to this article within our community.
For the ones that contacted me, you were right, the repository in GitHub was set as private. It's set to public now: GitHub Repository - Examples
Announcement
Anastasia Dyubaylo · Nov 13, 2021
Hey Community,
The new video is worth watching on InterSystems Developers YouTube:
⏯ Data Warehousing with InterSystems
Get a quick tour of classic and emerging terms used to describe logical and physical data stores that go beyond exclusive application use. With these concepts in mind, see how InterSystems® technology helps streamline your data landscape.
🗣 Presenter: @Benjamin.DeBoe, InterSystems Product Manager
Subscribe to InterSystems Developers YouTube and stay tuned!
Article
Guillaume Rongier · Feb 7, 2022
# 1. interoperability-embedded-python
This proof of concept aims to show how the **iris interoperability framework** can be used with **embedded python**.
## 1.1. Table of Contents
- [1. interoperability-embedded-python](#1-interoperability-embedded-python)
- [1.1. Table of Contents](#11-table-of-contents)
- [1.2. Example](#12-example)
- [1.3. Register a component](#13-register-a-component)
- [2. Demo](#2-demo)
- [3. Prerequisites](#3-prerequisites)
- [4. Installation](#4-installation)
- [4.1. With Docker](#41-with-docker)
- [4.2. Without Docker](#42-without-docker)
- [4.3. With ZPM](#43-with-zpm)
- [4.4. With PyPI](#44-with-pypi)
- [4.4.1. Known issues](#441-known-issues)
- [5. How to Run the Sample](#5-how-to-run-the-sample)
- [5.1. Docker containers](#51-docker-containers)
- [5.2. Management Portal and VSCode](#52-management-portal-and-vscode)
- [5.3. Open the production](#53-open-the-production)
- [6. What's inside the repository](#6-whats-inside-the-repository)
- [6.1. Dockerfile](#61-dockerfile)
- [6.2. .vscode/settings.json](#62-vscodesettingsjson)
- [6.3. .vscode/launch.json](#63-vscodelaunchjson)
- [6.4. .vscode/extensions.json](#64-vscodeextensionsjson)
- [6.5. src folder](#65-src-folder)
- [7. How it works](#7-how-it-works)
- [7.1. The `__init__.py`file](#71-the-__init__pyfile)
- [7.2. The `common` class](#72-the-common-class)
- [7.3. The `business_host` class](#73-the-business_host-class)
- [7.4. The `inbound_adapter` class](#74-the-inbound_adapter-class)
- [7.5. The `outbound_adapter` class](#75-the-outbound_adapter-class)
- [7.6. The `business_service` class](#76-the-business_service-class)
- [7.7. The `business_process` class](#77-the-business_process-class)
- [7.8. The `business_operation` class](#78-the-business_operation-class)
- [7.8.1. The dispacth system](#781-the-dispacth-system)
- [7.8.2. The methods](#782-the-methods)
- [7.9. The `director` class](#79-the-director-class)
- [7.10. The `objects`](#710-the-objects)
- [7.11. The `messages`](#711-the-messages)
- [7.12. How to regsiter a component](#712-how-to-regsiter-a-component)
- [7.12.1. register\_component](#7121-register_component)
- [7.12.2. register\_file](#7122-register_file)
- [7.12.3. register\_folder](#7123-register_folder)
- [7.12.4. migrate](#7124-migrate)
- [7.12.4.1. setting.py file](#71241-settingpy-file)
- [7.12.4.1.1. CLASSES section](#712411-classes-section)
- [7.12.4.1.2. Productions section](#712412-productions-section)
- [7.13. Direct use of Grongier.PEX](#713-direct-use-of-grongierpex)
- [8. Command line](#8-command-line)
- [8.1. help](#81-help)
- [8.2. default](#82-default)
- [8.3. lists](#83-lists)
- [8.4. start](#84-start)
- [8.5. kill](#85-kill)
- [8.6. stop](#86-stop)
- [8.7. restart](#87-restart)
- [8.8. migrate](#88-migrate)
- [8.9. export](#89-export)
- [8.10. status](#810-status)
- [8.11. version](#811-version)
- [8.12. log](#812-log)
- [9. Credits](#9-credits)
## 1.2. Example
```python
from grongier.pex import BusinessOperation,Message
class MyBusinessOperation(BusinessOperation):
def on_init(self):
#This method is called when the component is becoming active in the production
self.log_info("[Python] ...MyBusinessOperation:on_init() is called")
return
def on_teardown(self):
#This method is called when the component is becoming inactive in the production
self.log_info("[Python] ...MyBusinessOperation:on_teardown() is called")
return
def on_message(self, message_input:MyRequest):
# called from service/process/operation, message is of type MyRequest with property request_string
self.log_info("[Python] ...MyBusinessOperation:on_message() is called with message:"+message_input.request_string)
response = MyResponse("...MyBusinessOperation:on_message() echos")
return response
@dataclass
class MyRequest(Message):
request_string:str = None
@dataclass
class MyResponse(Message):
my_string:str = None
```
## 1.3. Register a component
Thanks to the method grongier.pex.Utils.register_component() :
Start an embedded python shell :
```sh
/usr/irissys/bin/irispython
```
Then use this class method to add a python class to the component list for interoperability.
```python
from grongier.pex import Utils
Utils.register_component(,,,,)
```
e.g :
```python
from grongier.pex import Utils
Utils.register_component("MyCombinedBusinessOperation","MyCombinedBusinessOperation","/irisdev/app/src/python/demo/",1,"PEX.MyCombinedBusinessOperation")
```
This is a hack, this not for production.
# 2. Demo
The demo can be found inside `src/python/demo/reddit/` and is composed of :
- An `adapter.py` file that holds a `RedditInboundAdapter` that will, given a service, fetch Reddit recent posts.
- A `bs.py` file that holds three `services` that does the same thing, they will call our `Process` and send it reddit post. One work on his own, one use the `RedditInBoundAdapter` we talked about earlier and the last one use a reddit inbound adapter coded in ObjectScript.
- A `bp.py` file that holds a `FilterPostRoutingRule` process that will analyze our reddit posts and send it to our `operations` if it contains certain words.
- A `bo.py` file that holds :
- Two **email operations** that will send a mail to a certain company depending on the words analyzed before, one works on his own and the other one works with an OutBoundAdapter.
- Two **file operations** that will write in a text file depending on the words analyzed before, one works on his own and the other one works with an OutBoundAdapter.
New json trace for python native messages :
# 3. Prerequisites
Make sure you have [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) and [Docker desktop](https://www.docker.com/products/docker-desktop) installed.
# 4. Installation
## 4.1. With Docker
Clone/git pull the repo into any local directory
```sh
git clone https://github.com/grongierisc/interpeorability-embedded-python
```
Open the terminal in this directory and run:
```sh
docker-compose build
```
Run the IRIS container with your project:
```sh
docker-compose up -d
```
## 4.2. Without Docker
Install the *grongier_pex-1.2.4-py3-none-any.whl* on you local iris instance :
```sh
/usr/irissys/bin/irispython -m pip install grongier_pex-1.2.4-py3-none-any.whl
```
Then load the ObjectScript classes :
```ObjectScript
do $System.OBJ.LoadDir("/opt/irisapp/src","cubk","*.cls",1)
```
## 4.3. With ZPM
```objectscript
zpm "install pex-embbeded-python"
```
## 4.4. With PyPI
```sh
pip3 install iris_pex_embedded_python
```
Import the ObjectScript classes, open an embedded python shell and run :
```python
from grongier.pex import Utils
Utils.setup()
```
### 4.4.1. Known issues
If the module is not updated, make sure to remove the old version :
```sh
pip3 uninstall iris_pex_embedded_python
```
or manually remove the `grongier` folder in `/lib/python/`
or force the installation with pip :
```sh
pip3 install --upgrade iris_pex_embedded_python --target /lib/python/
```
# 5. How to Run the Sample
## 5.1. Docker containers
In order to have access to the InterSystems images, we need to go to the following url: http://container.intersystems.com. After connecting with our InterSystems credentials, we will get our password to connect to the registry. In the docker VScode addon, in the image tab, by pressing connect registry and entering the same url as before (http://container.intersystems.com) as a generic registry, we will be asked to give our credentials. The login is the usual one but the password is the one we got from the website.
From there, we should be able to build and compose our containers (with the `docker-compose.yml` and `Dockerfile` files given).
## 5.2. Management Portal and VSCode
This repository is ready for [VS Code](https://code.visualstudio.com/).
Open the locally-cloned `interoperability-embedeed-python` folder in VS Code.
If prompted (bottom right corner), install the recommended extensions.
**IMPORTANT**: When prompted, reopen the folder inside the container so you will be able to use the python components within it. The first time you do this it may take several minutes while the container is readied.
By opening the folder remote you enable VS Code and any terminals you open within it to use the python components within the container. Configure these to use `/usr/irissys/bin/irispython`
## 5.3. Open the production
To open the production you can go to [production](http://localhost:52773/csp/irisapp/EnsPortal.ProductionConfig.zen?PRODUCTION=PEX.Production).
You can also click on the bottom on the `127.0.0.1:52773[IRISAPP]` button and select `Open Management Portal` then, click on [Interoperability] and [Configure] menus then click [productions] and [Go].
The production already has some code sample.
Here we can see the production and our pure python services and operations:
New json trace for python native messages :
# 6. What's inside the repository
## 6.1. Dockerfile
A dockerfile which install some python dependancies (pip, venv) and sudo in the container for conviencies.
Then it create the dev directory and copy in it this git repository.
It starts IRIS and activates **%Service_CallIn** for **Python Shell**.
Use the related docker-compose.yml to easily setup additional parametes like port number and where you map keys and host folders.
This dockerfile ends with the installation of requirements for python modules.
Use .env/ file to adjust the dockerfile being used in docker-compose.
## 6.2. .vscode/settings.json
Settings file to let you immedietly code in VSCode with [VSCode ObjectScript plugin](https://marketplace.visualstudio.com/items?itemName=daimor.vscode-objectscript)
## 6.3. .vscode/launch.json
Config file if you want to debug with VSCode ObjectScript
[Read about all the files in this article](https://community.intersystems.com/post/dockerfile-and-friends-or-how-run-and-collaborate-objectscript-projects-intersystems-iris)
## 6.4. .vscode/extensions.json
Recommendation file to add extensions if you want to run with VSCode in the container.
[More information here](https://code.visualstudio.com/docs/remote/containers)

This is very useful to work with embedded python.
## 6.5. src folder
```
src
├── Grongier
│ └── PEX // ObjectScript classes that wrap python code
│ ├── BusinessOperation.cls
│ ├── BusinessProcess.cls
│ ├── BusinessService.cls
│ ├── Common.cls
│ ├── Director.cls
│ ├── InboundAdapter.cls
│ ├── Message.cls
│ ├── OutboundAdapter.cls
│ ├── Python.cls
│ ├── Test.cls
│ └── _utils.cls
├── PEX // Some example of wrapped classes
│ └── Production.cls
└── python
├── demo // Actual python code to run this demo
| `-- reddit
| |-- adapter.py
| |-- bo.py
| |-- bp.py
| |-- bs.py
| |-- message.py
| `-- obj.py
├── dist // Wheel used to implement python interoperability components
│ └── grongier_pex-1.2.4-py3-none-any.whl
├── grongier
│ └── pex // Helper classes to implement interoperability components
│ ├── _business_host.py
│ ├── _business_operation.py
│ ├── _business_process.py
│ ├── _business_service.py
│ ├── _common.py
│ ├── _director.py
│ ├── _inbound_adapter.py
│ ├── _message.py
│ ├── _outbound_adapter.py
│ ├── __init__.py
│ └── _utils.py
└── setup.py // setup to build the wheel
```
# 7. How it works
## 7.1. The `__init__.py`file
This file will allow us to create the classes to import in the code.
It gets from the multiple files seen earlier the classes and make them into callable classes.
That way, when you wish to create a business operation, for example, you can just do:
```python
from grongier.pex import BusinessOperation
```
## 7.2. The `common` class
The common class shouldn't be called by the user, it defines almost all the other classes.
This class defines:
`on_init`: The on_init() method is called when the component is started. Use the on_init() method to initialize any structures needed by the component.
`on_tear_down`: Called before the component is terminated. Use it to free any structures.
`on_connected`: The on_connected() method is called when the component is connected or reconnected after being disconnected.Use the on_connected() method to initialize any structures needed by the component.
`log_info`: Write a log entry of type "info". :log entries can be viewed in the management portal.
`log_alert`: Write a log entry of type "alert". :log entries can be viewed in the management portal.
`log_warning`: Write a log entry of type "warning". :log entries can be viewed in the management portal.
`log_error`: Write a log entry of type "error". :log entries can be viewed in the management portal.
## 7.3. The `business_host` class
The business host class shouldn't be called by the user, it is the base class for all the business classes.
This class defines:
`send_request_sync`: Send the specified message to the target business process or business operation synchronously.
**Parameters**:
- **target**: a string that specifies the name of the business process or operation to receive the request.
The target is the name of the component as specified in the Item Name property in the production definition, not the class name of the component.
- **request**: specifies the message to send to the target. The request is either an instance of a class that is a subclass of Message class or of IRISObject class.
If the target is a build-in ObjectScript component, you should use the IRISObject class. The IRISObject class enables the PEX framework to convert the message to a class supported by the target.
- **timeout**: an optional integer that specifies the number of seconds to wait before treating the send request as a failure. The default value is -1, which means wait forever.
description: an optional string parameter that sets a description property in the message header. The default is None.
**Returns**:
the response object from target.
**Raises**:
TypeError: if request is not of type Message or IRISObject.
`send_request_async`: Send the specified message to the target business process or business operation asynchronously.
**Parameters**:
- **target**: a string that specifies the name of the business process or operation to receive the request.
The target is the name of the component as specified in the Item Name property in the production definition, not the class name of the component.
- **request**: specifies the message to send to the target. The request is an instance of IRISObject or of a subclass of Message.
If the target is a built-in ObjectScript component, you should use the IRISObject class. The IRISObject class enables the PEX framework to convert the message to a class supported by the target.
- **description**: an optional string parameter that sets a description property in the message header. The default is None.
**Raises**:
TypeError: if request is not of type Message or IRISObject.
`get_adapter_type`: Name of the registred Adapter.
## 7.4. The `inbound_adapter` class
Inbound Adapter in Python are subclass from grongier.pex.InboundAdapter in Python, that inherit from all the functions of the [common class](#72-the-common-class).
This class is responsible for receiving the data from the external system, validating the data, and sending it to the business service by calling the BusinessHost process_input method.
This class defines:
`on_task`: Called by the production framework at intervals determined by the business service CallInterval property.
The message can have any structure agreed upon by the inbound adapter and the business service.
Example of an inbound adapter ( situated in the src/python/demo/reddit/adapter.py file ):
```python
from grongier.pex import InboundAdapter
import requests
import iris
import json
class RedditInboundAdapter(InboundAdapter):
"""
This adapter use requests to fetch self.limit posts as data from the reddit
API before calling process_input for each post.
"""
def on_init(self):
if not hasattr(self,'feed'):
self.feed = "/new/"
if self.limit is None:
raise TypeError('no Limit field')
self.last_post_name = ""
return 1
def on_task(self):
self.log_info(f"LIMIT:{self.limit}")
if self.feed == "" :
return 1
tSC = 1
# HTTP Request
try:
server = "https://www.reddit.com"
request_string = self.feed+".json?before="+self.last_post_name+"&limit="+self.limit
self.log_info(server+request_string)
response = requests.get(server+request_string)
response.raise_for_status()
data = response.json()
updateLast = 0
for key, value in enumerate(data['data']['children']):
if value['data']['selftext']=="":
continue
post = iris.cls('dc.Reddit.Post')._New()
post._JSONImport(json.dumps(value['data']))
post.OriginalJSON = json.dumps(value)
if not updateLast:
self.LastPostName = value['data']['name']
updateLast = 1
response = self.BusinessHost.ProcessInput(post)
except requests.exceptions.HTTPError as err:
if err.response.status_code == 429:
self.log_warning(err.__str__())
else:
raise err
except Exception as err:
self.log_error(err.__str__())
raise err
return tSC
```
## 7.5. The `outbound_adapter` class
Outbound Adapter in Python are subclass from grongier.pex.OutboundAdapter in Python, that inherit from all the functions of the [common class](#72-the-common-class).
This class is responsible for sending the data to the external system.
The Outbound Adapter gives the Operation the possibility to have a heartbeat notion.
To activate this option, the CallInterval parameter of the adapter must be strictly greater than 0.
Example of an outbound adapter ( situated in the src/python/demo/reddit/adapter.py file ):
```python
class TestHeartBeat(OutboundAdapter):
def on_keepalive(self):
self.log_info('beep')
def on_task(self):
self.log_info('on_task')
```
## 7.6. The `business_service` class
This class is responsible for receiving the data from external system and sending it to business processes or business operations in the production.
The business service can use an adapter to access the external system, which is specified overriding the get_adapter_type method.
There are three ways of implementing a business service:
- Polling business service with an adapter - The production framework at regular intervals calls the adapter’s OnTask() method,
which sends the incoming data to the the business service ProcessInput() method, which, in turn calls the OnProcessInput method with your code.
- Polling business service that uses the default adapter - In this case, the framework calls the default adapter's OnTask method with no data.
The OnProcessInput() method then performs the role of the adapter and is responsible for accessing the external system and receiving the data.
- Nonpolling business service - The production framework does not initiate the business service. Instead custom code in either a long-running process
or one that is started at regular intervals initiates the business service by calling the Director.CreateBusinessService() method.
Business service in Python are subclass from grongier.pex.BusinessService in Python, that inherit from all the functions of the [business host](#73-the-business_host-class).
This class defines:
`on_process_input`: Receives the message from the inbond adapter via the PRocessInput method and is responsible for forwarding it to target business processes or operations.
If the business service does not specify an adapter, then the default adapter calls this method with no message and the business service is responsible for receiving the data from the external system and validating it.
**Parameters**:
- **message_input**: an instance of IRISObject or subclass of Message containing the data that the inbound adapter passes in.
The message can have any structure agreed upon by the inbound adapter and the business service.
Example of a business service ( situated in the src/python/demo/reddit/bs.py file ):
```python
from grongier.pex import BusinessService
import iris
from message import PostMessage
from obj import PostClass
class RedditServiceWithPexAdapter(BusinessService):
"""
This service use our python Python.RedditInboundAdapter to receive post
from reddit and call the FilterPostRoutingRule process.
"""
def get_adapter_type():
"""
Name of the registred Adapter
"""
return "Python.RedditInboundAdapter"
def on_process_input(self, message_input):
msg = iris.cls("dc.Demo.PostMessage")._New()
msg.Post = message_input
return self.send_request_sync(self.target,msg)
def on_init(self):
if not hasattr(self,'target'):
self.target = "Python.FilterPostRoutingRule"
return
```
## 7.7. The `business_process` class
Typically contains most of the logic in a production.
A business process can receive messages from a business service, another business process, or a business operation.
It can modify the message, convert it to a different format, or route it based on the message contents.
The business process can route a message to a business operation or another business process.
Business processes in Python are subclass from grongier.pex.BusinessProcess in Python, that inherit from all the functions of the [business host](#73-the-business_host-class).
This class defines:
`on_request`: Handles requests sent to the business process. A production calls this method whenever an initial request for a specific business process arrives on the appropriate queue and is assigned a job in which to execute.
**Parameters**:
- **request**: An instance of IRISObject or subclass of Message that contains the request message sent to the business process.
**Returns**:
An instance of IRISObject or subclass of Message that contains the response message that this business process can return
to the production component that sent the initial message.
`on_response`: Handles responses sent to the business process in response to messages that it sent to the target.
A production calls this method whenever a response for a specific business process arrives on the appropriate queue and is assigned a job in which to execute.
Typically this is a response to an asynchronous request made by the business process where the responseRequired parameter has a true value.
**Parameters**:
- **request**: An instance of IRISObject or subclass of Message that contains the initial request message sent to the business process.
- **response**: An instance of IRISObject or subclass of Message that contains the response message that this business process can return to the production component that sent the initial message.
- **callRequest**: An instance of IRISObject or subclass of Message that contains the request that the business process sent to its target.
- **callResponse**: An instance of IRISObject or subclass of Message that contains the incoming response.
- **completionKey**: A string that contains the completionKey specified in the completionKey parameter of the outgoing SendAsync() method.
**Returns**:
An instance of IRISObject or subclass of Message that contains the response message that this business process can return
to the production component that sent the initial message.
`on_complete`: Called after the business process has received and handled all responses to requests it has sent to targets.
**Parameters**:
- **request**: An instance of IRISObject or subclass of Message that contains the initial request message sent to the business process.
- **response**: An instance of IRISObject or subclass of Message that contains the response message that this business process can return to the production component that sent the initial message.
**Returns**:
An instance of IRISObject or subclass of Message that contains the response message that this business process can return to the production component that sent the initial message.
Example of a business process ( situated in the src/python/demo/reddit/bp.py file ):
```python
from grongier.pex import BusinessProcess
from message import PostMessage
from obj import PostClass
class FilterPostRoutingRule(BusinessProcess):
"""
This process receive a PostMessage containing a reddit post.
It then understand if the post is about a dog or a cat or nothing and
fill the right infomation inside the PostMessage before sending it to
the FileOperation operation.
"""
def on_init(self):
if not hasattr(self,'target'):
self.target = "Python.FileOperation"
return
def on_request(self, request):
if 'dog'.upper() in request.post.selftext.upper():
request.to_email_address = 'dog@company.com'
request.found = 'Dog'
if 'cat'.upper() in request.post.selftext.upper():
request.to_email_address = 'cat@company.com'
request.found = 'Cat'
if request.found is not None:
return self.send_request_sync(self.target,request)
else:
return
```
## 7.8. The `business_operation` class
This class is responsible for sending the data to an external system or a local system such as an iris database.
The business operation can optionally use an adapter to handle the outgoing message which is specified overriding the get_adapter_type method.
If the business operation has an adapter, it uses the adapter to send the message to the external system.
The adapter can either be a PEX adapter, an ObjectScript adapter or a [python adapter](#75-the-outbound_adapter-class).
Business operation in Python are subclass from grongier.pex.BusinessOperation in Python, that inherit from all the functions of the [business host](#73-the-business_host-class).
### 7.8.1. The dispacth system
In a business operation it is possbile to create any number of function [similar to the on_message method](#782-the-methods) that will take as argument a [typed request](#711-the-messages) like this `my_special_message_method(self,request: MySpecialMessage)`.
The dispatch system will automatically analyze any request arriving to the operation and dispacth the requests depending of their type. If the type of the request is not recognized or is not specified in any **on_message like function**, the dispatch system will send it to the `on_message` function.
### 7.8.2. The methods
This class defines:
`on_message`: Called when the business operation receives a message from another production component [that can not be dispatched to another function](#781-the-dispacth-system).
Typically, the operation will either send the message to the external system or forward it to a business process or another business operation.
If the operation has an adapter, it uses the Adapter.invoke() method to call the method on the adapter that sends the message to the external system.
If the operation is forwarding the message to another production component, it uses the SendRequestAsync() or the SendRequestSync() method.
**Parameters**:
- **request**: An instance of either a subclass of Message or of IRISObject containing the incoming message for the business operation.
**Returns**:
The response object
Example of a business operation ( situated in the src/python/demo/reddit/bo.py file ):
```python
from grongier.pex import BusinessOperation
from message import MyRequest,MyMessage
import iris
import os
import datetime
import smtplib
from email.mime.text import MIMEText
class EmailOperation(BusinessOperation):
"""
This operation receive a PostMessage and send an email with all the
important information to the concerned company ( dog or cat company )
"""
def my_message(self,request:MyMessage):
sender = 'admin@example.com'
receivers = 'toto@example.com'
port = 1025
msg = MIMEText(request.toto)
msg['Subject'] = 'MyMessage'
msg['From'] = sender
msg['To'] = receivers
with smtplib.SMTP('localhost', port) as server:
server.sendmail(sender, receivers, msg.as_string())
print("Successfully sent email")
def on_message(self, request):
sender = 'admin@example.com'
receivers = [ request.to_email_address ]
port = 1025
msg = MIMEText('This is test mail')
msg['Subject'] = request.found+" found"
msg['From'] = 'admin@example.com'
msg['To'] = request.to_email_address
with smtplib.SMTP('localhost', port) as server:
# server.login('username', 'password')
server.sendmail(sender, receivers, msg.as_string())
print("Successfully sent email")
```
If this operation is called using a MyRequest message, the my_message function will be called thanks to the dispatcher, otherwise the on_message function will be called.
## 7.9. The `director` class
The Director class is used for nonpolling business services, that is, business services which are not automatically called by the production framework (through the inbound adapter) at the call interval.
Instead these business services are created by a custom application by calling the Director.create_business_service() method.
This class defines:
`create_business_service`: The create_business_service() method initiates the specified business service.
**Parameters**:
- **connection**: an IRISConnection object that specifies the connection to an IRIS instance for Java.
- **target**: a string that specifies the name of the business service in the production definition.
**Returns**:
an object that contains an instance of IRISBusinessService
`start_production`: The start_production() method starts the production.
**Parameters**:
- **production_name**: a string that specifies the name of the production to start.
`stop_production`: The stop_production() method stops the production.
**Parameters**:
- **production_name**: a string that specifies the name of the production to stop.
`restart_production`: The restart_production() method restarts the production.
**Parameters**:
- **production_name**: a string that specifies the name of the production to restart.
`list_productions`: The list_productions() method returns a dictionary of the names of the productions that are currently running.
## 7.10. The `objects`
We will use `dataclass` to hold information in our [messages](#711-the-messages) in a `obj.py` file.
Example of an object ( situated in the src/python/demo/reddit/obj.py file ):
```python
from dataclasses import dataclass
@dataclass
class PostClass:
title: str
selftext : str
author: str
url: str
created_utc: float = None
original_json: str = None
```
## 7.11. The `messages`
The messages will contain one or more [objects](#710-the-objects), located in the `obj.py` file.
Messages, requests and responses all inherit from the `grongier.pex.Message` class.
These messages will allow us to transfer information between any business service/process/operation.
Example of a message ( situated in the src/python/demo/reddit/message.py file ):
```python
from grongier.pex import Message
from dataclasses import dataclass
from obj import PostClass
@dataclass
class PostMessage(Message):
post:PostClass = None
to_email_address:str = None
found:str = None
```
WIP It is to be noted that it is needed to use types when you define an object or a message.
## 7.12. How to regsiter a component
You can register a component to iris in many way :
* Only one component with `register_component`
* All the component in a file with `register_file`
* All the component in a folder with `register_folder`
### 7.12.1. register_component
Start an embedded python shell :
```sh
/usr/irissys/bin/irispython
```
Then use this class method to add a new py file to the component list for interoperability.
```python
from grongier.pex import Utils
Utils.register_component(,,,,)
```
e.g :
```python
from grongier.pex import Utils
Utils.register_component("MyCombinedBusinessOperation","MyCombinedBusinessOperation","/irisdev/app/src/python/demo/",1,"PEX.MyCombinedBusinessOperation")
```
### 7.12.2. register_file
Start an embedded python shell :
```sh
/usr/irissys/bin/irispython
```
Then use this class method to add a new py file to the component list for interoperability.
```python
from grongier.pex import Utils
Utils.register_file(,,)
```
e.g :
```python
from grongier.pex import Utils
Utils.register_file("/irisdev/app/src/python/demo/bo.py",1,"PEX")
```
### 7.12.3. register_folder
Start an embedded python shell :
```sh
/usr/irissys/bin/irispython
```
Then use this class method to add a new py file to the component list for interoperability.
```python
from grongier.pex import Utils
Utils.register_folder(,,)
```
e.g :
```python
from grongier.pex import Utils
Utils.register_folder("/irisdev/app/src/python/demo/",1,"PEX")
```
### 7.12.4. migrate
Start an embedded python shell :
```sh
/usr/irissys/bin/irispython
```
Then use this static method to migrate the settings file to the iris framework.
```python
from grongier.pex import Utils
Utils.migrate()
```
#### 7.12.4.1. setting.py file
This file is used to store the settings of the interoperability components.
It has two sections :
* `CLASSES` : This section is used to store the classes of the interoperability components.
* `PRODUCTIONS` : This section is used to store the productions of the interoperability components.
e.g :
```python
import bp
from bo import *
from bs import *
CLASSES = {
'Python.RedditService': RedditService,
'Python.FilterPostRoutingRule': bp.FilterPostRoutingRule,
'Python.FileOperation': FileOperation,
'Python.FileOperationWithIrisAdapter': FileOperationWithIrisAdapter,
}
PRODUCTIONS = [
{
'dc.Python.Production': {
"@Name": "dc.Demo.Production",
"@TestingEnabled": "true",
"@LogGeneralTraceEvents": "false",
"Description": "",
"ActorPoolSize": "2",
"Item": [
{
"@Name": "Python.FileOperation",
"@Category": "",
"@ClassName": "Python.FileOperation",
"@PoolSize": "1",
"@Enabled": "true",
"@Foreground": "false",
"@Comment": "",
"@LogTraceEvents": "true",
"@Schedule": "",
"Setting": {
"@Target": "Host",
"@Name": "%settings",
"#text": "path=/tmp"
}
},
{
"@Name": "Python.RedditService",
"@Category": "",
"@ClassName": "Python.RedditService",
"@PoolSize": "1",
"@Enabled": "true",
"@Foreground": "false",
"@Comment": "",
"@LogTraceEvents": "false",
"@Schedule": "",
"Setting": [
{
"@Target": "Host",
"@Name": "%settings",
"#text": "limit=10\nother
Announcement
Anastasia Dyubaylo · Jun 25, 2021
Hi Community,
Enjoy watching the new video on InterSystems Developers YouTube:
⏯ Installing InterSystems API Manager
See the installation process for InterSystems API Manager 1.5. Learn how to prepare an InterSystems IRIS database platform instance and host system, and see how to run scripts provided in the API Manager installation kit.
Related chapters in InterSystems Documentation:
Installation instructions: IAM Guide
Configuring secure connections: Using TLS with InterSystems IRIS
In this video:
Preparing an InterSystems IRIS instance
Preparing a host system for API Manager
Running API Manager setup scripts
Testing and viewing the Admin Portal
Enjoy and stay tuned! 👍🏼
Article
Crys · Aug 12, 2021
Being equipped by science and technology, human being have walked a long way by great inventions such as steam-engines or aeroplannes; while after decades, people gradually recognize that single creation could not lauch an industry-boom again. That is why and when, technologies grow up with a community, where are we now=P. An eco-system of technology would be born with the power of a system and grow up with the power of system-science, such as InterSystems, with which seated the letters "s-y-s-t-e-m". Graduated with M.S. of Information Systems, I've been deeply recognized the power of a system, in which is there the planet of Earth itself.
Inter-discinplines with Computer Science and Health Care need quite a lot considerations. Difficulties are all-over and everywhere, which conducts very few contributor and explorers since it's too hard. Struggling in the dealing with issues in the area of health care by IT-solutions, encountered me the Ensemble. Out of my imagination, brought me it to a great world. The interoperability with consensus on Hl7 launches the IT-ecosystem towards integration. System Science tell-s us the power of Systems Thinking, while the Ensemble show-s us. That was the point where I found a good set of facilities and tools. Later on, together with Ensemble, IRIS and ai- machineLearning are also compatible on CloudComputing. Gradually being clear-er and clearer, the interoperability single "tree" has grown up into a forest/ecosystem like a base-electricity factory supplying electricPower. World is always going too fast to be predicted. For a world built by Cncf- CloudNativeS, the basic infrastruture by interoperability would be the core contributor in supporting a booming technological and scientific world=P.
{p.s. grammer mistakes there or not. fixing and enjoying.} GPT-3 generated? ) i'm telling u: No. keep smiling pls.
Question
Bren Mochocki · Aug 16, 2021
It appears the link to download IRIS is down:https://download.intersystems.com/download/login.csp
Thanks for the heads up @Bren.Mochocki! This is being looked into as we speak. No eta on when it will be restored as of right now.... @Bren.Mochocki , the link worked for me today; downloading is back. Try it again, please, and post back if you still have troubles.
Erik