<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eduard</title>
    <description>The latest articles on DEV Community by Eduard (@eduard93).</description>
    <link>https://dev.to/eduard93</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F415448%2F75621d55-fb30-4215-b64c-08d81a5f30e9.jpeg</url>
      <title>DEV Community: Eduard</title>
      <link>https://dev.to/eduard93</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eduard93"/>
    <language>en</language>
    <item>
      <title>How to work with InterSystems IRIS from DataGrip</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Mon, 24 Jan 2022 19:52:30 +0000</pubDate>
      <link>https://dev.to/intersystems/how-to-work-with-intersystems-iris-from-datagrip-3kdl</link>
      <guid>https://dev.to/intersystems/how-to-work-with-intersystems-iris-from-datagrip-3kdl</guid>
      <description>&lt;p&gt;&lt;a href="https://www.jetbrains.com/datagrip/"&gt;DataGrip&lt;/a&gt; is a multi-engine database environment targeting the specific needs of professional SQL developers, DataGrip makes working with databases an enjoyable and productive experience.&lt;/p&gt;

&lt;p&gt;To work with InterSystems IRIS from DataGrip you'll need to add InterSystems JDBC driver first (once per DataGrip) and after that add all your InterSystems IRIS connections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1: Add InterSystems IRIS JDBC Driver&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1. Go To File → DataSources&lt;/p&gt;

&lt;p&gt;2. Go to + → Driver&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H5uZ6C-j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image%281270%29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H5uZ6C-j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image%281270%29.png" alt="" width="175" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3. Set Driver properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Name: InterSystems IRIS&lt;/li&gt;
&lt;li&gt;  Class: &lt;code&gt;com.intersystems.jdbc.IRISDriver&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Add JDBC Driver file: path to &lt;code&gt;/&amp;lt;IRIS&amp;gt;/dev/java/lib/JDK18/intersystems-jdbc-3.2.0.jar&lt;/code&gt; (version can change, also if you don't have InterSystems IRIS installed, you can download the drivers &lt;a href="https://github.com/intersystems-community/iris-driver-distribution"&gt;here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;  Add URL Template: Basic &lt;code&gt;jdbc:IRIS://{host}[:{port}]/{database}&lt;/code&gt; (you can add additional parameters as described &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=BJAVA_CONNECTING"&gt;here&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y9Ujocea--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image%281271%29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y9Ujocea--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image%281271%29.png" alt="" width="880" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4. Click &lt;code&gt;OK&lt;/code&gt; to save the driver definition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 2: Add InterSystems IRIS Instance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1. In the same DataSources window go to +→InterSystems IRIS&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VE-Vun9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image%281272%29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VE-Vun9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image%281272%29.png" alt="" width="182" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2. Specify connection settings depending on your instance and click &lt;code&gt;Test Connection&lt;/code&gt;. You'll see an error or connection successful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--78tsYckb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image%281273%29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--78tsYckb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image%281273%29.png" alt="" width="723" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It it's a new local installation the defaults likely are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Host: localhost&lt;/li&gt;
&lt;li&gt;  Port: 1972&lt;/li&gt;
&lt;li&gt;  User: _SYSTEM&lt;/li&gt;
&lt;li&gt;  Password: SYS&lt;/li&gt;
&lt;li&gt;  Database: USER&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3. Click &lt;code&gt;OK&lt;/code&gt; to save.&lt;/p&gt;

&lt;p&gt;That's it! Now you can query and explore InterSystems IRIS through Datagrip.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Deploying a sharded InterSystems IRIS cluster with Docker and MergeCPF</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Thu, 14 Oct 2021 16:17:27 +0000</pubDate>
      <link>https://dev.to/intersystems/deploying-a-sharded-intersystems-iris-cluster-with-docker-and-mergecpf-3369</link>
      <guid>https://dev.to/intersystems/deploying-a-sharded-intersystems-iris-cluster-with-docker-and-mergecpf-3369</guid>
      <description>&lt;p&gt;In this article, we will run an InterSystems IRIS cluster using docker and &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_cpf#RACS_cpf_edit_merge"&gt;Merge CPF files&lt;/a&gt; - a new feature allowing you to configure servers with ease.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On UNIX® and Linux, you can modify the default iris.cpf using a declarative CPF merge file. A merge file is a partial CPF that sets the desired values for any number of parameters upon instance startup. The CPF merge operation works only once for each instance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our cluster architecture is very simple, it would consist of one Node1 (master node) and two Data Nodes (&lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_ShardRole"&gt;check all available roles&lt;/a&gt;). Unfortunately, &lt;code&gt;docker-compose&lt;/code&gt; cannot deploy to several servers (although it can deploy to remote hosts), so this is useful for local development of sharding-aware data models,  tests, and such. For a productive InterSystems IRIS Cluster deployment, you should use either &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GICM_using"&gt;ICM&lt;/a&gt; or &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AIKO"&gt;IKO&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker-compose.yml
&lt;/h2&gt;

&lt;p&gt;Let's start with docker-compose configuration:&lt;/p&gt;

&lt;p&gt;docker-compose.yml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;iris1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containers.intersystems.com/intersystems/iris:2020.3.0.221.0&lt;/span&gt;
    &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--key /ISC/iris.key&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;iris1&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ISC_DATA_DIRECTORY=/ISC/iris.sys.d/sys1&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ISC_CPF_MERGE_FILE=/ISC/CPF2merge-master-instance.conf&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./:/ISC:delegated&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9011:1972&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9012:52773&lt;/span&gt;

  &lt;span class="na"&gt;iris2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containers.intersystems.com/intersystems/iris:2020.3.0.221.0&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--key /ISC/iris.key --before 'sleep 60'&lt;/span&gt;
    &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;iris2&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ISC_DATA_DIRECTORY=/ISC/iris.sys.d/sys2&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ISC_CPF_MERGE_FILE=/ISC/CPF2merge-data-instance.conf&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./:/ISC:delegated&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;iris1&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9021:1972&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9022:52773&lt;/span&gt;

  &lt;span class="na"&gt;iris3&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;containers.intersystems.com/intersystems/iris:2020.3.0.221.0&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--key /ISC/iris.key --before 'sleep 60'&lt;/span&gt;
    &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;iris3&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ISC_DATA_DIRECTORY=/ISC/iris.sys.d/sys3&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ISC_CPF_MERGE_FILE=/ISC/CPF2merge-data-instance.conf&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./:/ISC:delegated&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;iris1&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9031:1972&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9032:52773&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see we're running a default &lt;code&gt;intersystems/iris:2020.3.0.221.0&lt;/code&gt; image, providing the license key (it must support sharding), persisting the data using &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ADOCK#ADOCK_iris_durable"&gt;Durable %SYS&lt;/a&gt; feature, and provide &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=ADOCK#ADOCK_iris_running"&gt;ISC_CPF_MERGE_FILE&lt;/a&gt; pointing at our merge files (which are different for Node1 and Data Nodes). Additionally Data Nodes are started a minute late allowing Node1 to start and that's an extremely conservative estimate, on a decent hardware startup time takes seconds tops.&lt;/p&gt;

&lt;p&gt;Cluster configuration happens at CPF merge files, let's check them out&lt;/p&gt;

&lt;h2&gt;
  
  
  CPF2merge-data-instance.conf
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Startup]
PasswordHash=FBFE8593AEFA510C27FD184738D6E865A441DE98,u4ocm4qh
ShardRole=node1

[config]
MaxServerConn=64
MaxServers=64
globals=0,0,400,0,0,0
errlog=1000
routines=32
gmheap=256000
locksiz=1179648
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens here?&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;[Startup]&lt;/code&gt; part we enable Sharding by assigning Node1 role to our cluster. And in &lt;code&gt;[config]&lt;/code&gt;we expand our server a bit allowing more caches and connections. That's all!&lt;/p&gt;

&lt;h2&gt;
  
  
  CPF2merge-data-instance.conf
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Startup]

ShardClusterURL=IRIS://iris1:1972/IRISCLUSTER
ShardRole=DATA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For data nodes, we need to provide the URL of the Node1 and the node role.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/intersystems-ru/iris-container-recipes"&gt;Check the repository&lt;/a&gt; or run this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/intersystems-ru/iris-container-recipes.git
&lt;span class="nb"&gt;cd &lt;/span&gt;iris-container-recipes
&lt;span class="nb"&gt;cd &lt;/span&gt;cluster
// copy iris.key &lt;span class="k"&gt;in &lt;/span&gt;cluster folder
docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After starting the InterSystems IRIS cluster you can access it from the &lt;a href="http://localhost:9012/csp/sys/exp/%25CSP.UI.Portal.SQL.Home.zen?%24NAMESPACE=IRISCLUSTER"&gt;browser&lt;/a&gt;. User/pass is: &lt;code&gt;_SYSTEM&lt;/code&gt;/&lt;code&gt;SYS&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_cpf#RACS_cpf_edit_merge"&gt;Merge CPF files&lt;/a&gt; is a great and simple tool allowing you to configure InterSystems IRIS instances.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Continuous Delivery of your InterSystems solution using GitLab - Part VIII: CD using ICM</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Mon, 23 Aug 2021 11:45:59 +0000</pubDate>
      <link>https://dev.to/intersystems/continuous-delivery-of-your-intersystems-solution-using-gitlab-part-viii-cd-using-icm-37a4</link>
      <guid>https://dev.to/intersystems/continuous-delivery-of-your-intersystems-solution-using-gitlab-part-viii-cd-using-icm-37a4</guid>
      <description>&lt;p&gt;&lt;a href="https://community.intersystems.com/post/continuous-delivery-your-intersystems-solution-using-gitlab-index"&gt;In this series of articles&lt;/a&gt;, I'd like to present and discuss several possible approaches toward software development with InterSystems technologies and GitLab. I will cover such topics as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git 101&lt;/li&gt;
&lt;li&gt;Git flow (development process)&lt;/li&gt;
&lt;li&gt;GitLab installation&lt;/li&gt;
&lt;li&gt;GitLab Workflow&lt;/li&gt;
&lt;li&gt;Continuous Delivery&lt;/li&gt;
&lt;li&gt;GitLab installation and configuration&lt;/li&gt;
&lt;li&gt;GitLab CI/CD&lt;/li&gt;
&lt;li&gt;Why containers?&lt;/li&gt;
&lt;li&gt;Containers infrastructure&lt;/li&gt;
&lt;li&gt;CD using containers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CD using ICM&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we'll build Continuous Delivery with InterSystems Cloud Manager. ICM is a cloud provisioning and deployment solution for applications based on InterSystems IRIS. It allows you to define the desired deployment configuration and ICM would provision it automatically. For more information take a look at &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_icm"&gt;First Look: ICM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In our Continuous Delivery configuration we would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Push code into GitLab repository&lt;/li&gt;
&lt;li&gt;Build docker image&lt;/li&gt;
&lt;li&gt;Publish image to docker registry&lt;/li&gt;
&lt;li&gt;Test it on a test server&lt;/li&gt;
&lt;li&gt;If tests pass, deploy on a production server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or in graphical format:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a-vmtkDD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/asset-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a-vmtkDD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/asset-3.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see it's all pretty much the same except we would be using ICM instead of managing Docker containers manually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ICM configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before we can start upgrading containers, they should be provisioned. For that we need to define defaults.json and definitions.json, describing our architecture. I'll provide these 2 files for a LIVE server, definitions for a TEST server are the same, defaults are the same except for Tag and SystemMode values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;defaults.json:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GCP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gsdemo2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LIVE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SystemMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LIVE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DataVolumeSize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SSHUser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sample"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SSHPublicKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icmdata/ssh/insecure.pub"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"SSHPrivateKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icmdata/ssh/insecure"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DockerImage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eduard93/icmdemo:master"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DockerUsername"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eduard93"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DockerPassword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"TLSKeyDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icmdata/tls"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Credentials"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icmdata/gcp.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"elebedyu-test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"MachineType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"n1-standard-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Zone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"us-east1-b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rhel-cloud/rhel-7-v20170719"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ISCPassword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SYS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Mirror"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;definitions.json&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ISCLicense"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icmdata/iris.key"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the ICM container &lt;code&gt;/icmdata&lt;/code&gt; folder is mounted from the host and:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TEST server definitions are placed in &lt;code&gt;/icmdata/test&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;LIVE server definitions are placed in &lt;code&gt;/icmdata/live&lt;/code&gt; folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After obtaining all required keys:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;keygenSSH.sh /icmdata/ssh
keygenTLS.sh /icmdata/tls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And placing required files in &lt;code&gt;/icmdata&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;iris.key&lt;/li&gt;
&lt;li&gt;gcp.json (for deployment to Google Cloud Platform)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Call ICM to provision your instances:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /icmdata/test
icm provision
icm run
&lt;span class="nb"&gt;cd&lt;/span&gt; /icmdata/live
icm provision
icm run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would provision one TEST and one LIVE server with one standalone InterSystems IRIS instance on each.&lt;/p&gt;

&lt;p&gt;Please refer to &lt;a href="http://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_icm"&gt;ICM First Look&lt;/a&gt; for a more detailed guide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, we need to build our image.&lt;/p&gt;

&lt;p&gt;Our code would be, as usual, stored in the repository, CD configuration in &lt;code&gt;gitlab-ci.yml&lt;/code&gt; but in addition (to increase security) we would store several server-specific files on a build server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iris.key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;License key. Alternatively, it can be downloaded during container build rather than stored on a server. It's rather insecure to store in the repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pwd.txt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;File containing default password. Again, it's rather insecure to store it in the repository. Also, if you're hosting prod environment on a separate server it could have a different default password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;load_ci_icm.script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Initial script, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loads installer&lt;/li&gt;
&lt;li&gt;Installer does application initialization&lt;/li&gt;
&lt;li&gt;Loads the code
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
set dir = ##class(%File).NormalizeDirectory($system.Util.GetEnviron("CI_PROJECT_DIR"))
do ##class(%SYSTEM.OBJ).Load(dir _ "Installer/Global.cls","cdk")
do ##class(Installer.Global).init()
halt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the first line is intentionally left empty.&lt;/p&gt;

&lt;p&gt;Several things are different from previous examples. First of all we are not enabling OS Authentication as ICM would interact with container instead of GitLab directly. Second of all I'm using Installer manifest to initialize our application to show different approaches to initialization. Read more on Installer &lt;a href="https://community.intersystems.com/post/deploying-applications-intersystems-cache-installer"&gt;in this article&lt;/a&gt;. Finally we'll publish our image in a Docher Hub as a private repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installer/Global.cls&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our installer manifest looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Manifest&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Log&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Creating namespace ${Namespace}"&lt;/span&gt; &lt;span class="na"&gt;Level=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Namespace&lt;/span&gt; &lt;span class="na"&gt;Name=&lt;/span&gt;&lt;span class="s"&gt;"${Namespace}"&lt;/span&gt; &lt;span class="na"&gt;Create=&lt;/span&gt;&lt;span class="s"&gt;"yes"&lt;/span&gt; &lt;span class="na"&gt;Code=&lt;/span&gt;&lt;span class="s"&gt;"${Namespace}"&lt;/span&gt; &lt;span class="na"&gt;Ensemble=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;Data=&lt;/span&gt;&lt;span class="s"&gt;"IRISTEMP"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Database&lt;/span&gt; &lt;span class="na"&gt;Name=&lt;/span&gt;&lt;span class="s"&gt;"${Namespace}"&lt;/span&gt; &lt;span class="na"&gt;Dir=&lt;/span&gt;&lt;span class="s"&gt;"${MGRDIR}/${Namespace}"&lt;/span&gt; &lt;span class="na"&gt;Create=&lt;/span&gt;&lt;span class="s"&gt;"yes"&lt;/span&gt; &lt;span class="na"&gt;MountRequired=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;Resource=&lt;/span&gt;&lt;span class="s"&gt;"%DB_${Namespace}"&lt;/span&gt; &lt;span class="na"&gt;PublicPermissions=&lt;/span&gt;&lt;span class="s"&gt;"RW"&lt;/span&gt; &lt;span class="na"&gt;MountAtStartup=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Configuration&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;Import&lt;/span&gt; &lt;span class="na"&gt;File=&lt;/span&gt;&lt;span class="s"&gt;"${Dir}MyApp"&lt;/span&gt; &lt;span class="na"&gt;Recurse=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="na"&gt;Flags=&lt;/span&gt;&lt;span class="s"&gt;"cdk"&lt;/span&gt; &lt;span class="na"&gt;IgnoreErrors=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Namespace&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;Log&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Mapping to USER"&lt;/span&gt; &lt;span class="na"&gt;Level=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Namespace&lt;/span&gt; &lt;span class="na"&gt;Name=&lt;/span&gt;&lt;span class="s"&gt;"USER"&lt;/span&gt; &lt;span class="na"&gt;Create=&lt;/span&gt;&lt;span class="s"&gt;"no"&lt;/span&gt; &lt;span class="na"&gt;Code=&lt;/span&gt;&lt;span class="s"&gt;"USER"&lt;/span&gt; &lt;span class="na"&gt;Data=&lt;/span&gt;&lt;span class="s"&gt;"USER"&lt;/span&gt; &lt;span class="na"&gt;Ensemble=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;Configuration&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Log&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"Mapping MyApp package to USER namespace"&lt;/span&gt; &lt;span class="na"&gt;Level=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;ClassMapping&lt;/span&gt; &lt;span class="na"&gt;From=&lt;/span&gt;&lt;span class="s"&gt;"${Namespace}"&lt;/span&gt; &lt;span class="na"&gt;Package=&lt;/span&gt;&lt;span class="s"&gt;"MyApp"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/Configuration&amp;gt;&lt;/span&gt;

        &lt;span class="nt"&gt;&amp;lt;CSPApplication&lt;/span&gt;  &lt;span class="na"&gt;Url=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;      &lt;span class="na"&gt;Directory=&lt;/span&gt;&lt;span class="s"&gt;"${Dir}client"&lt;/span&gt; &lt;span class="na"&gt;AuthenticationMethods=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt; &lt;span class="na"&gt;IsNamespaceDefault=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; &lt;span class="na"&gt;Grant=&lt;/span&gt;&lt;span class="s"&gt;"%ALL"&lt;/span&gt;  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;CSPApplication&lt;/span&gt;  &lt;span class="na"&gt;Url=&lt;/span&gt;&lt;span class="s"&gt;"/myApp"&lt;/span&gt; &lt;span class="na"&gt;Directory=&lt;/span&gt;&lt;span class="s"&gt;"${Dir}"&lt;/span&gt;       &lt;span class="na"&gt;AuthenticationMethods=&lt;/span&gt;&lt;span class="s"&gt;"64"&lt;/span&gt; &lt;span class="na"&gt;IsNamespaceDefault=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt; &lt;span class="na"&gt;Grant=&lt;/span&gt;&lt;span class="s"&gt;"%ALL"&lt;/span&gt;  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/Namespace&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Manifest&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it implements the following changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates application Namespace.&lt;/li&gt;
&lt;li&gt;Creates application code database (data would be stored in USER database).&lt;/li&gt;
&lt;li&gt;loads code into application code database.&lt;/li&gt;
&lt;li&gt;Maps MyApp package to USER namespace.&lt;/li&gt;
&lt;li&gt;Creates 2 web applications: for HTML and for REST.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;gitlab-ci.yml&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, to Continuous Delivery configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cp -r /InterSystems/mount ci&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd ci&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo 'SuperUser' | cat - pwd.txt load_ci_icm.script &amp;gt; temp.txt&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mv temp.txt load_ci.script&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd ..&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker build --build-arg CI_PROJECT_DIR=$CI_PROJECT_DIR -t eduard93/icmdemo:$CI_COMMIT_REF_NAME .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is going on here?&lt;/p&gt;

&lt;p&gt;First of all, as &lt;a href="https://docs.docker.com/engine/reference/commandline/build/"&gt;docker build&lt;/a&gt; can access only subdirectories of a base build directory - in our case repository root, we need to copy our "secret" directory (the one with &lt;code&gt;iris.key&lt;/code&gt;, &lt;code&gt;pwd.txt&lt;/code&gt; and &lt;code&gt;load_ci_icm.script&lt;/code&gt;) into the cloned repository.&lt;/p&gt;

&lt;p&gt;Next, first terminal access requires a user/pass so we'd add them to &lt;code&gt;load_ci.script&lt;/code&gt; (that's what empty line at the beginning of &lt;code&gt;load_ci.script&lt;/code&gt; is for btw).&lt;/p&gt;

&lt;p&gt;Finally, we build docker image and tag it appropriately:  &lt;code&gt;eduard93/icmdemo:$CI_COMMIT_REF_NAME&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;where &lt;code&gt;$CI_COMMIT_REF_NAME&lt;/code&gt; is the name of a current branch. Note that the first part of the image tag should be named same as project name in GitLab, so it could be seen in GitLab Registry tab (instructions on tagging are available in Registry tab).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Building a docker image is done using &lt;a href="https://docs.docker.com/engine/reference/builder/"&gt;Dockerfile&lt;/a&gt;, here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; intersystems/iris:2018.1.1-released&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; SRC_DIR=/tmp/src&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CI_DIR=$SRC_DIR/ci&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; CI_PROJECT_DIR=$SRC_DIR&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./ $SRC_DIR&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$CI_DIR&lt;/span&gt;/iris.key &lt;span class="nv"&gt;$ISC_PACKAGE_INSTALLDIR&lt;/span&gt;/mgr/ &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$CI_DIR&lt;/span&gt;/GitLab.xml &lt;span class="nv"&gt;$ISC_PACKAGE_INSTALLDIR&lt;/span&gt;/mgr/ &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$ISC_PACKAGE_INSTALLDIR&lt;/span&gt;/dev/Cloud/ICM/changePassword.sh &lt;span class="nv"&gt;$CI_DIR&lt;/span&gt;/pwd.txt &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; iris start &lt;span class="nv"&gt;$ISC_PACKAGE_INSTANCENAME&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; irissession &lt;span class="nv"&gt;$ISC_PACKAGE_INSTANCENAME&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt;%SYS &amp;lt; &lt;span class="nv"&gt;$CI_DIR&lt;/span&gt;/load_ci.script &lt;span class="se"&gt;\
&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; iris stop &lt;span class="nv"&gt;$ISC_PACKAGE_INSTANCENAME&lt;/span&gt; quietly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start from the basic iris container.&lt;/p&gt;

&lt;p&gt;First of all, we copy our repository (and "secret" directory) inside the container.&lt;/p&gt;

&lt;p&gt;Next, we copy license key to mgr directory.&lt;/p&gt;

&lt;p&gt;Then we change the password to the value from pwd.txt. Note that pwd.txt is deleted in this operation.&lt;/p&gt;

&lt;p&gt;After that, the instance is started and load_ci.script is executed.&lt;/p&gt;

&lt;p&gt;Finally, iris instance is stopped.&lt;/p&gt;

&lt;p&gt;Note that I'm using &lt;a href="https://docs.gitlab.com/runner/executors/"&gt;GitLab Shell executor&lt;/a&gt; and not Docker executor. Docker executor is used when you need something from inside of the image, for example, you're building an Android application in java container and you only need an apk. In our case, we need a whole container and for that, we need Shell executor. So we're running Docker commands via GitLab Shell executor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Publish&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let's publish our image in a Docker Hub&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;publish image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;publish&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker login -u eduard93 -p ${DOCKERPASSWORD}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push eduard93/icmdemo:$CI_COMMIT_REF_NAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note &lt;code&gt;${DOCKERPASSWORD}&lt;/code&gt; variable, it's a GitLab &lt;a href="https://docs.gitlab.com/ee/ci/variables/#protected-variables"&gt;secret variable&lt;/a&gt;. We can add them in GitLab &amp;gt; Project &amp;gt; Settings &amp;gt; CI/CD &amp;gt; Variables:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8eN_lznR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/vars.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8eN_lznR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/vars.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Job logs also do not contain password value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Running with gitlab-runner 10.6.0 &lt;span class="o"&gt;(&lt;/span&gt;a3543a27&lt;span class="o"&gt;)&lt;/span&gt;
  on icm 82634fd1
Using Shell executor...
Running on docker...
Fetching changes...
Removing ci/
HEAD is now at 8e24591 Add deploy to LIVE
Checking out 8e245910 as master...
Skipping Git submodules setup
&lt;span class="nv"&gt;$ &lt;/span&gt;docker login &lt;span class="nt"&gt;-u&lt;/span&gt; eduard93 &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOCKERPASSWORD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
WARNING! Using &lt;span class="nt"&gt;--password&lt;/span&gt; via the CLI is insecure. Use &lt;span class="nt"&gt;--password-stdin&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Login Succeeded
&lt;span class="nv"&gt;$ &lt;/span&gt;docker push eduard93/icmdemo:&lt;span class="nv"&gt;$CI_COMMIT_REF_NAME&lt;/span&gt;
The push refers to repository &lt;span class="o"&gt;[&lt;/span&gt;docker.io/eduard93/icmdemo]
master: digest: sha256:d1612811c11154e77c84f0c08a564a3edeb7ddbbd9b7acb80754fda97f95d101 size: 2620
Job succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and on Docker Hub we can see our new image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6-rvJzdA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6-rvJzdA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/d.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have our image, next let's run it on our test server. Here is the script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;run image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;run&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_COMMIT_REF_NAME&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker exec icm sh -c "cd /icmdata/test &amp;amp;&amp;amp; icm upgrade -image eduard93/icmdemo:$CI_COMMIT_REF_NAME"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With ICM we need to run only one command (&lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GICM_using#GICM_using_deploy_manage_icmupgrade"&gt;icm upgrade&lt;/a&gt;) to upgrade existing deployment. We're calling it by running &lt;code&gt;docker exec icm sh -c ...&lt;/code&gt; which executes a specified command inside the icm container.  First we mode into &lt;code&gt;/icmdata/test&lt;/code&gt; where our ICM deployment definition is defined for a TEST server. After that we call &lt;code&gt;icm upgrade&lt;/code&gt; to replace currently existing container with a new container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's run some tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;test image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker exec icm sh -c "cd /icmdata/test &amp;amp;&amp;amp; icm session -namespace USER -command 'do \$classmethod(\"%UnitTest.Manager\",\"RunTest\",\"MyApp/Tests\",\"/nodelete\")' | tee /dev/stderr | grep 'All PASSED' &amp;amp;&amp;amp; exit 0 || exit 1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, we're executing one command inside our icm container. icm session executes a command on a deployed node. The command runs unit tests. After that it pipes all output to the screen and also to grep to find Unit Tests results and exit the process successfully or with an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deploy on a Production server is absolutely the same as deploy on test, except for another directory for the LIVE deployment definition. In the case where tests failed this stage would not be executed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deploy image:
  stage: deploy
  environment:
    name: $CI_COMMIT_REF_NAME
  tags:
    - master
  script:
    - docker exec icm sh -c "cd /icmdata/live &amp;amp;&amp;amp; icm upgrade -image eduard93/icmdemo:$CI_COMMIT_REF_NAME"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ICM gives you a simple, intuitive way to provision cloud infrastructure and deploy services on it, helping you get into the cloud now without major development or retooling. The benefits of infrastructure as code (IaC) and containerized deployment make it easy to deploy InterSystems IRIS-based applications on public cloud platforms such as Google, Amazon, and Azure, or on your private VMware vSphere cloud. Define what you want, issue a few commands, and ICM does the rest.&lt;/p&gt;

&lt;p&gt;Even if you are already using cloud infrastructure, containers, or both, ICM dramatically reduces the time and effort required to provision and deploy your application by automating numerous otherwise manual steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/intersystems-ru/GitLab/tree/master/icm"&gt;Code for the article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://gitlab.eduard.win/test/docker"&gt;Test project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GICM"&gt;ICM Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GICM_using"&gt;First Look: ICM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Importing code into InterSystems IRIS from external libraries</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Tue, 13 Jul 2021 11:47:15 +0000</pubDate>
      <link>https://dev.to/intersystems/importing-code-into-intersystems-iris-from-external-libraries-3n0f</link>
      <guid>https://dev.to/intersystems/importing-code-into-intersystems-iris-from-external-libraries-3n0f</guid>
      <description>&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;Recently I reread &lt;a href="https://community.intersystems.com/post/story-support-how-quest-raw-deflate-compressiondecompression-function-leads-node-callout-server"&gt;this article&lt;/a&gt; by @Bernd.Mueller. It's about calling DELFATE function from &lt;a href="https://zlib.net/"&gt;zlib library&lt;/a&gt;. In this article I'll demonstrate several different approaches to callout libraries, we'll build the same functionality (compress function) in several different languages and compare them.&lt;/p&gt;



&lt;h1&gt;
  
  
  NodeJS
&lt;/h1&gt;

&lt;p&gt;Let's start with NodeJS. I'm taking the code almost directly from Bernd's article, except it does not use files, but rather direct http connection to pass data. For production use, it would be better to pass request as a body and encode both request and response as base64. Still, here's the &lt;a href="https://github.com/intersystems-ru/zlibisc/blob/master/node/zlibserver.js"&gt;code&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//zlibserver.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zlib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zlib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

 &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/zlibapi/:text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;        
        &lt;span class="nx"&gt;zlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deflate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;binary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="c1"&gt;// handle error&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;zlibserver started&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start it execute in OS bash (assuming &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt; installed):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;repo&amp;gt;&lt;span class="se"&gt;\n&lt;/span&gt;ode
npm &lt;span class="nb"&gt;install
&lt;/span&gt;node  ./zlibserver.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're running on port 3000, reading input string from request and returning compressed data in response as is. On a Caché side &lt;a href="https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GNET_http"&gt;http request&lt;/a&gt; is used to interact with this api:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// NodeJS implementation
/// do ##class(isc.zlib.Test).node()
ClassMethod node(text As %String = "Hello World", Output response As %String) As %Status
{
    kill response
    set req = ##class(%Net.HttpRequest).%New()
    set req.Server = "localhost"
    set req.Port = 3000
    set req.Location = "/zlibapi/" _ text
    set sc = req.Get(,,$$$NO)
    quit:$$$ISERR(sc) sc
    set response = req.HttpResponse.Data.Read($$$MaxStringLength)
    quit sc
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note, that I'm setting the third argument &lt;code&gt;set sc = req.Get(,,$$$NO)&lt;/code&gt; - &lt;code&gt;reset&lt;/code&gt; to zero. If you're writing interface to the outside http(s) server it's best to reuse one request object and just modify it as needed to perform new requests.&lt;/p&gt;

&lt;h1&gt;
  
  
  Java
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=EJVG"&gt;Java Gateway&lt;/a&gt; allows calling arbitrary Java code. Coincidently Java has &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/zip/Deflater.html"&gt;Deflater class&lt;/a&gt; which does exactly what we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;isc.zlib&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Arrays&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.zip.Deflater&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Java&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;compress&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;inputString&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;inputString&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;()*&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Encode a String into bytes&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inputString&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Compress the bytes&lt;/span&gt;

            &lt;span class="nc"&gt;Deflater&lt;/span&gt; &lt;span class="n"&gt;compresser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Deflater&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;compresser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setInput&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;compresser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;finish&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;compressedDataLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compresser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deflate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;compresser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyOfRange&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compressedDataLength&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UnsupportedEncodingException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// handle&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;


        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem with this implementation is that it returns &lt;code&gt;byte[]&lt;/code&gt; which becomes a Stream on Caché side. I have tried to return a string, but hadn't been able to found how to form proper binary string from &lt;code&gt;byte[]&lt;/code&gt;. If you have any ideas please leave a comment. &lt;br&gt;
To run it place jar from &lt;a href="https://github.com/intersystems-ru/zlibisc/releases"&gt;releases page&lt;/a&gt; into &lt;code&gt;&amp;lt;instance&amp;gt;/bin&lt;/code&gt; folder, load ObjectScript code into your instance and execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;write $System.Status.GetErrorText(##class(isc.zlib.Utils).createGateway())
write $System.Status.GetErrorText(##class(isc.zlib.Utils).updateJar())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check &lt;code&gt;createGateway&lt;/code&gt; method before running the command. Second argument &lt;code&gt;javaHome&lt;/code&gt; assumes that &lt;code&gt;JAVA_HOME&lt;/code&gt; environment variable is set. If it does not, specify home of Java 1.8 JRE as a second argument. After installing run this code to get compressed &lt;code&gt;text&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set gateway = ##class(isc.zlib.Utils).connect()
set response = ##class(isc.zlib.Java).compress(gateway, text)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  C
&lt;/h1&gt;

&lt;p&gt;An InterSystems &lt;a href="https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=BGCL_library"&gt;Callout library&lt;/a&gt; is a shared library that contains your custom Callout functions and the enabling code that allows Caché to use them.&lt;/p&gt;

&lt;p&gt;Here's our Callout library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define ZF_DLL
&lt;/span&gt;
&lt;span class="c1"&gt;// Ugly Windows hack&lt;/span&gt;
&lt;span class="cp"&gt;#ifndef ulong
&lt;/span&gt;   &lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;ulong&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="cp"&gt;#endif
&lt;/span&gt;
&lt;span class="cp"&gt;#include "string.h"
#include "stdio.h"
#include "stdlib.h"
#include "zlib.h"
#include &amp;lt;cdzf.h&amp;gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;istream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CACHE_EXSTRP&lt;/span&gt; &lt;span class="n"&gt;retval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="n"&gt;srcLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;istream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// +1 for the trailing `\0`&lt;/span&gt;
    &lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="n"&gt;destLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compressBound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;srcLen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//  estimate size needed for the buffer&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ostream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;destLen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ostream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;destLen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;istream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;srcLen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;CACHEEXSTRKILL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retval&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;CACHEEXSTRNEW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;destLen&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ZF_FAILURE&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
    &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;retval&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ostream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;destLen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// copy to retval-&amp;gt;str.ch&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ZF_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;ZFBEGIN&lt;/span&gt;
    &lt;span class="n"&gt;ZFENTRY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Compress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"cJ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Compress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ZFEND&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run it place &lt;code&gt;dll&lt;/code&gt; or &lt;code&gt;so&lt;/code&gt; files from &lt;a href="https://github.com/intersystems-ru/zlibisc/releases"&gt;releases page&lt;/a&gt; into &lt;code&gt;&amp;lt;instance&amp;gt;/bin&lt;/code&gt; folder. Repository also contains build scripts for Windows and Linux, execute them to build your own version.&lt;br&gt;
Linux prerequisites:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;build-essential zlib1g zlib1g-devel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows prerequisites: &lt;a href="http://win-builds.org/doku.php"&gt;WinBuilds&lt;/a&gt; - comes with zlib.&lt;/p&gt;

&lt;p&gt;To interact with callout library execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set path =  ##class(isc.zlib.Test).getLibPath() //get path to library file
set response = $ZF(-3, path, "Compress", text)       // execute function
do $ZF(-3, "")                                  //unload library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  System
&lt;/h1&gt;

&lt;p&gt;A little unexpected in an article about callout mechanisms, but Caché also has built-in &lt;a href="https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&amp;amp;LIBRARY=%25SYS&amp;amp;CLASSNAME=%25SYSTEM.Util#METHOD_Compress"&gt;Compress&lt;/a&gt; (and Decompress function). Call it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set response = $extract($SYSTEM.Util.Compress(text), 2, *-1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember that searching the docs or asking the questions here on the community may save you some time.&lt;/p&gt;

&lt;h1&gt;
  
  
  Comparison
&lt;/h1&gt;

&lt;p&gt;I have run simple tests (1Kb text, 1 000 000 iterations) on Linux and Windows and got these results.&lt;/p&gt;

&lt;p&gt;Windows: &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Callout&lt;/th&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Java&lt;/th&gt;
&lt;th&gt;Node&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time&lt;/td&gt;
&lt;td&gt;22,77&lt;/td&gt;
&lt;td&gt;33,41&lt;/td&gt;
&lt;td&gt;152,73&lt;/td&gt;
&lt;td&gt;622,51&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed (Kb/s)&lt;/td&gt;
&lt;td&gt;43912&lt;/td&gt;
&lt;td&gt;29927&lt;/td&gt;
&lt;td&gt;6547&lt;/td&gt;
&lt;td&gt;1606&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overhead, %&lt;/td&gt;
&lt;td&gt;-/-&lt;/td&gt;
&lt;td&gt;46,73%&lt;/td&gt;
&lt;td&gt;570,75%&lt;/td&gt;
&lt;td&gt;2633,90%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Linux:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Callout&lt;/th&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Java&lt;/th&gt;
&lt;th&gt;Node&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time&lt;/td&gt;
&lt;td&gt;76,35&lt;/td&gt;
&lt;td&gt;76,49&lt;/td&gt;
&lt;td&gt;147,24&lt;/td&gt;
&lt;td&gt;953,73&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed (Kb/s)&lt;/td&gt;
&lt;td&gt;13097&lt;/td&gt;
&lt;td&gt;13072&lt;/td&gt;
&lt;td&gt;6791&lt;/td&gt;
&lt;td&gt;1049&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overhead, %&lt;/td&gt;
&lt;td&gt;-/-&lt;/td&gt;
&lt;td&gt;0,19%&lt;/td&gt;
&lt;td&gt;92%&lt;/td&gt;
&lt;td&gt;1149%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To run tests load code and call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;do ##class(isc.zlib.Test).test(textLength, iterations)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;With InterSystems products, you can easily leverage existing code in other languages. However, choosing correct implementation is not always easy, you need to take several metrics into account, such as development speed, performance, and maintainability. Do you need to run on different operating systems?  Finding answers to these questions can help you decide on the best implementation plan.&lt;/p&gt;

&lt;h1&gt;
  
  
  Links
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/intersystems-ru/zlibisc/"&gt;Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/intersystems-ru/zlibisc/releases"&gt;Binaries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GNET_http"&gt;Http requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=EJVG"&gt;Java Gateway&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=BGCL_library"&gt;Callout library&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&amp;amp;LIBRARY=%25SYS&amp;amp;CLASSNAME=%25SYSTEM.Util#METHOD_Compress"&gt;Compress function&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Class Queries in InterSystems IRIS</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Wed, 03 Mar 2021 11:04:30 +0000</pubDate>
      <link>https://dev.to/intersystems/class-queries-in-intersystems-iris-4546</link>
      <guid>https://dev.to/intersystems/class-queries-in-intersystems-iris-4546</guid>
      <description>&lt;p&gt;&lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GOBJ_queries"&gt;Class Queries&lt;/a&gt; in InterSystems IRIS (and Cache, Ensemble, HealthShare) is a useful tool that separates SQL queries from Object Script code. Basically, it works like this: suppose that you want to use the same SQL query with different arguments in several different places.In this case you can avoid code duplication by declaring the query body as a class query and then calling this query by name. This approach is also convenient for custom queries, in which the task of obtaining the next row is defined by a developer. Sounds interesting? Then read on!&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic class queries
&lt;/h2&gt;

&lt;p&gt;Simply put, basic class queries allow you to represent SQL SELECT queries. SQL optimizer and compiler handle them just as they would standard SQL queries, but they are more convenient when it comes to executing them from Caché Object Script context. They are declared as Query items in class definitions (similar to Method or Property) in the following way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Type: &lt;a href="http://docs.intersystems.com/cache20152/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&amp;amp;LIBRARY=%25SYS&amp;amp;CLASSNAME=%25Library.SQLQuery"&gt;%SQLQuery&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  All arguments of your SQL query must be listed in the list of arguments&lt;/li&gt;
&lt;li&gt;  Query type: SELECT&lt;/li&gt;
&lt;li&gt;  Use the colon to access each argument (similar to static SQL)&lt;/li&gt;
&lt;li&gt;  Define the ROWSPEC parameter which contains information about names and data types of the output results along with the order of fields&lt;/li&gt;
&lt;li&gt;  (Optional) Define the CONTAINID parameter which corresponds to the numeric order if the field containing ID. If you don't need to return ID, don't assign any values to CONTAINID&lt;/li&gt;
&lt;li&gt;  (Optional) Define the COMPILEMODE parameter which corresponds to the similar parameter in static SQL and specifies when the SQL expression must be compiled. When this parameter is set to IMMEDIATE (by default), the query will be compiled simultaneously with the class. When this parameter is set to DYNAMIC, the query will be compiled before its first execution (similar to dynamic SQL)&lt;/li&gt;
&lt;li&gt;  (Optional) Define the SELECTMODE parameter which specifies the format of the query results&lt;/li&gt;
&lt;li&gt;  Add the SqlProc property, if you want to call this query as an SQL procedure.&lt;/li&gt;
&lt;li&gt;  Set the SqlName property, if you want to rename the query. The default name of a query in SQL context is as follows: PackageName.ClassName_QueryName&lt;/li&gt;
&lt;li&gt;  Caché Studio provides the built-in wizard for creating class queries\
Sample definition of the Sample.Person class with the ByName query which returns all user names that begin with a specified letter
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class Sample.Person Extends %Persistent
{
Property Name As %String;
Property DOB As %Date;
Property SSN As %String;
Query ByName(name As %String = "") As %SQLQuery
    (ROWSPEC="ID:%Integer,Name:%String,DOB:%Date,SSN:%String",
     CONTAINID = 1, SELECTMODE = "RUNTIME",
     COMPILEMODE = "IMMEDIATE") [ SqlName = SP_Sample_By_Name, SqlProc ]
{
SELECT ID, Name, DOB, SSN
FROM Sample.Person
WHERE (Name %STARTSWITH :name)
ORDER BY Name
}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can call this query from Caché Object Script in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set statement=##class(%SQL.Statement).%New()
Set status=statement.%PrepareClassQuery("Sample.Person","ByName")
If $$$ISERR(status) {
    Do $system.OBJ.DisplayError(status)
}
Set resultset=statement.%Execute("A")
While resultset.%Next() {
    Write !, resultset.%Get("Name")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can obtain a resultset using the automatically generated method queryNameFunc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set resultset = ##class(Sample.Person).ByNameFunc("A")
While resultset.%Next() {
    Write !, resultset.%Get("Name")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query can also be called from SQLcontext in these two ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;Call&lt;/span&gt; &lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SP_Sample_By_Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;Select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;Sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SP_Sample_By_Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'A'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class can be found in the SAMPLES default Caché namespace And that's all about simple queries. Now let's proceed to custom ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom class queries
&lt;/h2&gt;

&lt;p&gt;Though basic class queries work fine in most cases, sometimes it is necessary to execute full control over the query behavior in applications, e.g.:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Sophisticated selection criteria. Since in custom queries you implement a Caché Object Script method that returns the next row on your own, these criteria can be as sophisticated as you require.&lt;/li&gt;
&lt;li&gt;  If data is accessible only via API in a format that you don't want to use&lt;/li&gt;
&lt;li&gt;  If data is stored in globals (without classes)&lt;/li&gt;
&lt;li&gt;  If you need to escalate rights in order to access data&lt;/li&gt;
&lt;li&gt;  If you need to call an external API in order to access data&lt;/li&gt;
&lt;li&gt;  If you need to gain access to the file system in order to access data&lt;/li&gt;
&lt;li&gt;  You need to perform additional operations before running the query (e.g. establish a connection, check permissions, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, how do you create custom class queries? First of all, you should define 4 methods which implement the entire workflow for your query, from initialization to destruction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  queryName --- provides information about a query (similar to basic class queries)&lt;/li&gt;
&lt;li&gt;  queryName*&lt;em&gt;Execute&lt;/em&gt;* --- constructs a query&lt;/li&gt;
&lt;li&gt;  queryName*&lt;em&gt;Fetch&lt;/em&gt;* --- obtains the next row result of a query&lt;/li&gt;
&lt;li&gt;  queryName*&lt;em&gt;Close&lt;/em&gt;* --- destructs a query&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's analyze these methods in more detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The queryName method&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The queryName method represents information about a query.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Type: %Query&lt;/li&gt;
&lt;li&gt;  Leave body blank&lt;/li&gt;
&lt;li&gt;  Define the ROWSPEC parameter which contains the information about names and data types of the output results along with the field order&lt;/li&gt;
&lt;li&gt;  (Optional) Define the CONTAINID parameter which corresponds to the numeric order if the field containing ID. If you don't return ID, don't assign any value to CONTAINID&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, let's create the AllRecords query (queryName = AllRecords, and the method is simply called AllRecords) which will be outputting all instances of the new persistent class Utils.CustomQuery, one by one. First, let's create a new persistent class Utils.CustomQuery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class Utils.CustomQuery Extends (%Persistent, %Populate){
Property Prop1 As %String;
Property Prop2 As %Integer;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's write the AllRecords query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query AllRecords() As %Query(CONTAINID = 1, ROWSPEC = "Id:%String,Prop1:%String,Prop2:%Integer") [ SqlName = AllRecords, SqlProc ]
{
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The queryNameExecute method&lt;/strong&gt;\&lt;br&gt;
The queryNameExecute method fully initializes a query. The signature of this method is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod queryNameExecute(ByRef qHandle As %Binary, args) As %Status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  qHandle is used for communication with other methods of the query implementation&lt;/li&gt;
&lt;li&gt;  This method must set qHandle into the state which will then be passed to the queryNameFetch method&lt;/li&gt;
&lt;li&gt;  qHandle can be set to OREF, variable or a multi-dimensional variable&lt;/li&gt;
&lt;li&gt;  args are additional parameters passed to the query. You can add as many args as you need (or don't use them at all)&lt;/li&gt;
&lt;li&gt;  The method must return query initialization status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But let's get back to our example. You are free to iterate through the extent in multiple ways (I will describe the basic working approaches for custom queries below), but as for this example let's iterate through the global using the &lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_forder#RCOS_B75165"&gt;$Order&lt;/a&gt; function. In this case, qHandle will be storing the current ID, and since we don't need any additional arguments, the arg argument is not required. The result looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod AllRecordsExecute(ByRef qHandle As %Binary) As %Status {
    Set qHandle = ""    Quit $$$OK
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The queryNameFetch method&lt;/strong&gt;\&lt;br&gt;
The queryNameFetch method returns a single result in &lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_flist#RCOS_B73639"&gt;$List&lt;/a&gt; form. The signature of this method is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod queryNameFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = queryNameExecute ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  qHandle is used for communication with other methods of the query implementation&lt;/li&gt;
&lt;li&gt;  When the query is executed, qHandle is being assigned values specified by queryNameExecute or by previous call of queryNameFetch.&lt;/li&gt;
&lt;li&gt;  Row will be set either to a value of &lt;a href="http://docs.intersystems.com/cache20152/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&amp;amp;LIBRARY=%25SYS&amp;amp;CLASSNAME=%25Library.List"&gt;%List&lt;/a&gt; or to an empty string, if all data has been processed&lt;/li&gt;
&lt;li&gt;  AtEnd must be set to 1, once the end of data is reached.&lt;/li&gt;
&lt;li&gt;  The PlaceAfter keyword identifies the method's position in the int code . The "Fetch" method must be positioned after the "Execute" method, but this is important only for &lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_esql"&gt;static SQL&lt;/a&gt;, i.e. &lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_esql#GSQL_esql_cursor"&gt;cursors&lt;/a&gt; inside queries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In general, the following operations are performed within this method:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Check whether we've reached the end of data&lt;/li&gt;
&lt;li&gt; If there is still some data left: Create a new %List and assign a value to the Row variable&lt;/li&gt;
&lt;li&gt; Otherwise, set AtEnd to 1&lt;/li&gt;
&lt;li&gt; Prepare qHandle for the next result fetch&lt;/li&gt;
&lt;li&gt; Return the status&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is how it will look like in our example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod AllRecordsFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status {
    #; Iterating through ^Utils.CustomQueryD
    #; Writing the next id to qHandle and writing the global's value with the new id into val
    Set qHandle = $Order(^Utils.CustomQueryD(qHandle),1,val)
    #; Checking whether there is any more data left
       If qHandle = "" {
        Set AtEnd = 1
        Set Row = ""
        Quit $$$OK
    }
    #; If not, create %List
    #; val = $Lb("", Prop1, Prop2) see Storage definition
    #; Row =$lb(Id,Prop1, Prop2)  see ROWSPEC for the AllRecords request
    Set Row = $Lb(qHandle, $Lg(val,2), $Lg(val,3))
    Quit $$$OK
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The queryNameClose method&lt;/strong&gt;\&lt;br&gt;
The queryNameClose method terminates the query, once all the data is obtained. The signature of this method is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod queryNameClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = queryNameFetch ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Caché executes this method after the final call to the queryNameFetch method&lt;/li&gt;
&lt;li&gt;  In other words, this is a query destructor&lt;/li&gt;
&lt;li&gt;  Therefore you should dispose all SQL cursors, queries and local variables in its implementation&lt;/li&gt;
&lt;li&gt;  The methods return the current status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our example, we have to delete the local variable qHandle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod AllRecordsClose(ByRef qHandle As %Binary) As %Status {
    Kill qHandle
    Quit $$$OK
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here we are! Once you compile the class, you will be able to use the AllRecords query from %SQL.Statement -- just as the basic class queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Iteration logic approaches for custom queries
&lt;/h2&gt;

&lt;p&gt;So, what approaches can be used for custom queries? In general, there exist 3 basic approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GGBL_using#GGBL_using_traversing"&gt;Iteration through a global&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_esql#GSQL"&gt;Static SQL&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_dynsql"&gt;Dynamic SQL&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Iteration through a global&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The approach is based on using $Order and similar functions for iteration through a global. It can be used in the following cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Data is stored in globals (without classes)&lt;/li&gt;
&lt;li&gt;  You want to reduce the number of glorefs is the code&lt;/li&gt;
&lt;li&gt;  The results must be/can be sorted by the global's subscript&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Static SQL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The approach is based on cursors and static SQL. This is used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Making the int code more readable&lt;/li&gt;
&lt;li&gt;  Making the work with cursors easier&lt;/li&gt;
&lt;li&gt;  Speeding up the compilation process (static SQL is included into the class query and is therefore compiled only once).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Cursors generated from queries of the %SQLQuery type are named automatically, e.g. Q14.&lt;/li&gt;
&lt;li&gt;  All cursors used within a class must have different names.&lt;/li&gt;
&lt;li&gt;  Error messages are related to the internal names of cursors which have an additional characters at the end of their names. For example, an error in cursor Q140 is actually caused by cursor Q14.&lt;/li&gt;
&lt;li&gt;  Use PlaceAfter and make sure that cursors are used in the same int routinewhere they have been declared.&lt;/li&gt;
&lt;li&gt;  INTO must be used in conjunction with FETCH, but not DECLARE.
Example of static SQL for Utils.CustomQuery:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query AllStatic() As %Query(CONTAINID = 1, ROWSPEC = "Id:%String,Prop1:%String,Prop2:%Integer") [ SqlName = AllStatic, SqlProc ]
{
}

ClassMethod AllStaticExecute(ByRef qHandle As %Binary) As %Status
{
    &amp;amp;sql(DECLARE C CURSOR FOR
        SELECT Id, Prop1, Prop2
        FROM Utils.CustomQuery
     )
     &amp;amp;sql(OPEN C)
    Quit $$$OK
}

ClassMethod AllStaticFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = AllStaticExecute ]
{
    #; INTO must be with FETCH
    &amp;amp;sql(FETCH C INTO :Id, :Prop1, :Prop2)
    #; Check if we reached end of data
    If (SQLCODE'=0) {
        Set AtEnd = 1
        Set Row = ""
        Quit $$$OK
    }
    Set Row = $Lb(Id, Prop1, Prop2)
    Quit $$$OK
}

ClassMethod AllStaticClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = AllStaticFetch ]
{
    &amp;amp;sql(CLOSE C)
    Quit $$$OK
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dynamic SQL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The approach is based on other class queries and dynamic SQL. This is reasonable when in addition to an SQL query itself, you also need to perform some additional operations, e.g. execute an SQL query in several namespaces or escalate permissions before running the query.&lt;/p&gt;

&lt;p&gt;Example of dynamic SQL for Utils.CustomQuery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query AllDynamic() As %Query(CONTAINID = 1, ROWSPEC = "Id:%String,Prop1:%String,Prop2:%Integer") [ SqlName = AllDynamic, SqlProc ]
{
}

ClassMethod AllDynamicExecute(ByRef qHandle As %Binary) As %Status
{
    Set qHandle = ##class(%SQL.Statement).%ExecDirect(,"SELECT * FROM Utils.CustomQuery")
    Quit $$$OK
}

ClassMethod AllDynamicFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status
{
    If qHandle.%Next()=0 {
        Set AtEnd = 1
        Set Row = ""
        Quit $$$OK
    }
    Set Row = $Lb(qHandle.%Get("Id"), qHandle.%Get("Prop1"), qHandle.%Get("Prop2"))
    Quit $$$OK
}

ClassMethod AllDynamicClose(ByRef qHandle As %Binary) As %Status
{
    Kill qHandle
    Quit $$$OK
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative approach: %SQL.CustomResultSet
&lt;/h2&gt;

&lt;p&gt;Alternatively, you can create a query by subclassing from the &lt;a href="http://docs.intersystems.com/cache20152/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&amp;amp;LIBRARY=%25SYS&amp;amp;CLASSNAME=%25SQL.CustomResultSet"&gt;%SQL.CustomResultSet&lt;/a&gt; class. Benefits of this approach are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Slight increase in speed&lt;/li&gt;
&lt;li&gt;  ROWSPEC is unnecessary, since all metadata is obtained from the class definition&lt;/li&gt;
&lt;li&gt;  Compliance with the object-oriented design principles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To create query from the subclass of %SQL.CustomResultSet class, make sure to perform the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Define the properties corresponding to the resulting fields&lt;/li&gt;
&lt;li&gt; Define the private properties where the query context will be stored&lt;/li&gt;
&lt;li&gt; Override the %OpenCursor method (similar to queryNameExecute) which initiates the context. In case of any errors, set %SQLCODE and %Message as well&lt;/li&gt;
&lt;li&gt; Override the %Next method (similar to queryNameFetch) which obtains the next result. Fill in the properties. The method returns 0 if all the data has been processed and 1 if some data is still remaining&lt;/li&gt;
&lt;li&gt; Override the %CloseCursor method (similar to queryNameClose) if necessary\
Example of %SQL.CustomResultSet for Utils.CustomQuery:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class Utils.CustomQueryRS Extends %SQL.CustomResultSet
{
Property Id As %String;
Property Prop1 As %String;
Property Prop2 As %Integer;
Method %OpenCursor() As %Library.Status
{
    Set ..Id = ""
    Quit $$$OK
}

Method %Next(ByRef sc As %Library.Status) As %Library.Integer [ PlaceAfter = %Execute ]
{
    Set sc = $$$OK
    Set ..Id = $Order(^Utils.CustomQueryD(..Id),1,val)
    Quit:..Id="" 0
    Set ..Prop1 = $Lg(val,2)
    Set ..Prop2 = $Lg(val,3)
    Quit $$$OK
}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can call it from InterSystems Object Script code in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Set resultset= ##class(Utils.CustomQueryRS).%New()
       While resultset.%Next() {
        Write resultset.Id,!
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another example is available in the SAMPLES namespace -- it's the &lt;a href="http://docs.intersystems.com/cache20152/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&amp;amp;LIBRARY=SAMPLES&amp;amp;CLASSNAME=Sample.CustomResultSet"&gt;Sample.CustomResultSet&lt;/a&gt; class which implements a query for Samples.Person.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Custom queries will help you to separate SQL expressions from Caché Object Script code and implement sophisticated behavior which can be too difficult for pure SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GOBJ_queries"&gt;Class queries&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GGBL_using#GGBL_using_traversing"&gt;Iteration through a global&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_esql#GSQL"&gt;Static SQL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GSQL_dynsql"&gt;Dynamic SQL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://docs.intersystems.com/cache20152/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&amp;amp;LIBRARY=%25SYS&amp;amp;CLASSNAME=%25SQL.CustomResultSet"&gt;%SQL.CustomResultSet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/eduard93/3a4b180ac23c482250c6/raw/5cb2f4f8fcd969f4b5d8358a1debc2727e827073/Utils.CustomQuery.xml"&gt;The Utils.CustomQuery class&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/eduard93/de0dc2043e02e113bc8c/raw/02dac2e2aa0b663deb731dd1856646338e27c884/Utils.CustomQueryRS.xml"&gt;The Utils.CustomQueryRS class&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The author would like to thank &lt;a href="https://community.intersystems.com/user/alexander-koblov"&gt;Alexander Koblov&lt;/a&gt; for his assistance in writing this article.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Logging using macros in InterSystems IRIS</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Fri, 29 Jan 2021 11:42:56 +0000</pubDate>
      <link>https://dev.to/intersystems/logging-using-macros-in-intersystems-iris-5dcn</link>
      <guid>https://dev.to/intersystems/logging-using-macros-in-intersystems-iris-5dcn</guid>
      <description>&lt;p&gt;In this article we will design and build a logging system based on macros in InterSystems IRIS. Some &lt;a href="https://community.intersystems.com/post/macros-intersystems-cach%C3%A9"&gt;info on macros&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logging system&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Logging system is a useful tool for monitoring the work of an application that saves a lot of time during debugging and monitoring. Our system would consist of two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storage class (for log records)&lt;/li&gt;
&lt;li&gt;Set of macros that automatically add a new record to the log&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lGZHDwm3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/RackMultipart20210129-4-1jzk6pb_html_49ac0cb03196381.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lGZHDwm3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/RackMultipart20210129-4-1jzk6pb_html_49ac0cb03196381.gif" alt=""&gt;&lt;/a&gt; &lt;strong&gt;Storage class&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's create a table of what we need to store and specify when this data can be obtained – during compilation or at runtime. This will be required when working on the second part of the system - macros, where we will aim to have as many loggable details during compilation as possible:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Information&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Obtained during&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Event type&lt;/td&gt;
&lt;td&gt;Compilation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Class name&lt;/td&gt;
&lt;td&gt;Compilation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method name&lt;/td&gt;
&lt;td&gt;Compilation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Arguments passed to a method&lt;/td&gt;
&lt;td&gt;Compilation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Line number in the cls source code&lt;/td&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Line number in the generated int code&lt;/td&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Username&lt;/td&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Date/Time&lt;/td&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message&lt;/td&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP address&lt;/td&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let's create an App.Log class containing the properties from the table above. When an App.Log object is created, User Name, Date/Time and IP address properties are filled out automatically.&lt;/p&gt;

&lt;p&gt;App.Log class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class App.Log Extends %Persistent

{

/// Type of event
Property EventType As %String(MAXLEN = 10, VALUELIST = ",NONE,FATAL,ERROR,WARN,INFO,STAT,DEBUG,RAW") [InitialExpression = "INFO"];

/// Name of class, where event happened
Property ClassName As %Dictionary.Classname(MAXLEN = 256);

/// Name of method, where event happened
Property MethodName As %String(MAXLEN = 128);

/// Line of int code
Property Source As %String(MAXLEN = 2000);

/// Line of cls code
Property SourceCLS As %String(MAXLEN = 2000);

/// IRIS user
Property UserName As %String(MAXLEN = 128) [InitialExpression = {$username}];

/// Arguments' values passed to method
Property Arguments As %String(MAXLEN = 32000, TRUNCATE = 1);

/// Date and time
Property TimeStamp As %TimeStamp [InitialExpression = {$zdt($h, 3, 1)}];

/// User message
Property Message As %String(MAXLEN = 32000, TRUNCATE = 1);

/// User IP address
Property ClientIPAddress As %String(MAXLEN = 32) [InitialExpression = {..GetClientAddress()}];

/// Determine user IP address
ClassMethod GetClientAddress()
{
  // %CSP.Session source is preferable
  #dim %request As %CSP.Request
  If ($d(%request)) {
    Return %request.CgiEnvs("REMOTE\_ADDR")
  }
  Return $system.Process.ClientIPAddress()
}

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Logging macros&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Usually, macros are stored in separate *.inc files containing their definitions. The necessary files can be included into classes using the Include MacroFileName command, which in this case will look as follows: Include App.LogMacro.&lt;/p&gt;

&lt;p&gt;To start, let's define the main macro that the user will add to their application's code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#define LogEvent(%type, %message) Do ##class(App.Log).AddRecord($$$CurrentClass, $$$CurrentMethod, $$$StackPlace, %type, $$$MethodArguments, %message)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This macro accepts two input arguments: Event Type and Message. The Message argument is defined by the user, but the Event Type parameter will require additional macros with different names that will automatically identify the event type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#define LogNone(%message) $$$LogEvent("NONE", %message)

#define LogError(%message) $$$LogEvent("ERROR", %message)

#define LogFatal(%message) $$$LogEvent("FATAL", %message)

#define LogWarn(%message) $$$LogEvent("WARN", %message)

#define LogInfo(%message) $$$LogEvent("INFO", %message)

#define LogStat(%message) $$$LogEvent("STAT", %message)

#define LogDebug(%message) $$$LogEvent("DEBUG", %message)

#define LogRaw(%message) $$$LogEvent("RAW", %message)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore, in order to perform logging, the user only needs to place the $$$LogError("Additional message") macro in the application code.&lt;br&gt;
 All we need to do now is to define the $$$CurrentClass, $$$CurrentMethod, $$$StackPlace, $$$MethodArguments macros. Let's start with the first three:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#define CurrentClass ##Expression($$$quote(%classname))

#define CurrentMethod ##Expression($$$quote(%methodname))

#define StackPlace $st($st(-1),"PLACE")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;%classname&lt;/code&gt;, &lt;code&gt;%methodname&lt;/code&gt; variables are described in the &lt;a href="http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCOS_macros"&gt;documentation&lt;/a&gt;. The &lt;a href="http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fstack"&gt;$stack&lt;/a&gt; function returns INT code line number. To convert it into CLS line number we can use this &lt;a href="https://community.intersystems.com/post/cls-location-int-code?page=16#comment-88716"&gt;code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's use the %Dictionary package to get a list of method arguments and their values. It contains all the information about the classes, including method descriptions. We are particularly interested in the %Dictionary.CompiledMethod class and its FormalSpecParsed property, which is a list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$lb($lb("Name","Classs","Type(Output/ByRef)","Default value "),...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;corresponding to the method signature. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod Test(a As %Integer = 1, ByRef b = 2, Output c)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will have the following FormalSpecParsed value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$lb(
  $lb("a","%Library.Integer","","1"),
  $lb("b","%Library.String","&amp;amp;","2"),
  $lb("c","%Library.String","*","")
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to make &lt;code&gt;$$$MethodArguments&lt;/code&gt; macro expand into the following code (for the Test method):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"a="_$g(a,"Null")_"; b="_$g(b,"Null")_"; c="_$g(c,"Null")_";"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To achieve this, we have to do the following during compilation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get a class name and a method name&lt;/li&gt;
&lt;li&gt;Open a corresponding instance of the &lt;code&gt;%Dictionary.CompiledMethod&lt;/code&gt; class and get its &lt;code&gt;FormalSpec&lt;/code&gt; property&lt;/li&gt;
&lt;li&gt;Convert it into a source code line&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's add corresponding methods to the App.Log class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod GetMethodArguments(ClassName As %String, MethodName As %String) As %String
{
    Set list = ..GetMethodArgumentsList(ClassName,MethodName)
    Set string = ..ArgumentsListToString(list)
    Return string
}

ClassMethod GetMethodArgumentsList(ClassName As %String, MethodName As %String) As %List
{
    Set result = ""
    Set def = ##class(%Dictionary.CompiledMethod).%OpenId(ClassName _ "||" _ MethodName)
    If ($IsObject(def)) {
        Set result = def.FormalSpecParsed
    }
    Return result
}

ClassMethod ArgumentsListToString(List As %List) As %String
{
    Set result = ""
    For i=1:1:$ll(List) {
        Set result = result _ $$$quote($s(i&amp;gt;1=0:"",1:"; ") _ $lg($lg(List,i))_"=")
        _ "_$g(" _ $lg($lg(List,i)) _ ","_$$$quote(..#Null)_")_"
        _$s(i=$ll(List)=0:"",1:$$$quote(";"))
    }
    Return result
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's now define the &lt;code&gt;$$$MethodArguments&lt;/code&gt; macro as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#define MethodArguments ##Expression(##class(App.Log).GetMethodArguments(%classname,%methodname))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use case&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, let's create an &lt;code&gt;App.Use&lt;/code&gt; class with a &lt;code&gt;Test&lt;/code&gt; method to demonstrate the capabilities of the logging system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Include App.LogMacro
Class App.Use [ CompileAfter = App.Log ]
{
/// Do ##class(App.Use).Test()
ClassMethod Test(a As %Integer = 1, ByRef b = 2)
{
    $$$LogWarn("Text")
}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a result, the &lt;code&gt;$$$LogWarn("Text")&lt;/code&gt; macro in the int code converts into the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Do ##class(App.Log).AddRecord("App.Use","Test",$st($st(-1),"PLACE"),"WARN","a="\_$g(a,"Null")\_"; b="\_$g(b,"Null")\_";", "Text")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execution of this code will create a new App.Log record:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m_sBgLuj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/snimok_17.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m_sBgLuj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/snimok_17.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Having created a logging system, here's some improvement ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First of all, a possibility to process object-type arguments since our current implementation only logs object oref.&lt;/li&gt;
&lt;li&gt;Second, a call to restore the context of a method from stored argument values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Processing of object-type arguments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The line that puts an argument value to the log is generated in the ArgumentsListToString method and looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"\_$g(" \_ $lg($lg(List,i)) \_ ","\_$$$quote(..#Null)\_")\_"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's do some refactoring and move it into a separate GetArgumentValue method that will accept a variable name and class (all of which we know from &lt;code&gt;FormalSpecParsed&lt;/code&gt;) and output a code that will convert the variable into a line. We'll use existing code for data types, and objects will be converted into JSON with the help of &lt;code&gt;SerializeObject&lt;/code&gt; (for calling from the user code) and &lt;code&gt;WriteJSONFromObject&lt;/code&gt; (for converting an object into JSON) methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod GetArgumentValue(Name As %String, ClassName As %Dictionary.CacheClassname) As %String
{
    If $ClassMethod(ClassName, "%Extends", "%RegisteredObject") {
        // it's an object
        Return "_##class(App.Log).SerializeObject("_Name _ ")_"
    } Else {
        // it's a datatype
        Return "_$g(" _ Name _ ","_$$$quote(..#Null)_")_"
    }
}

ClassMethod SerializeObject(Object) As %String
{
    Return:'$IsObject(Object) Object
    Return ..WriteJSONFromObject(Object)
}

ClassMethod WriteJSONFromObject(Object) As %String [ ProcedureBlock = 0 ]
{
    Set OldIORedirected = ##class(%Device).ReDirectIO()
    Set OldMnemonic = ##class(%Device).GetMnemonicRoutine()
    Set OldIO = $io
    Try {
        Set Str=""

        //Redirect IO to the current routine - makes use of the labels defined below
        Use $io::("^"_$ZNAME)

        //Enable redirection
        Do ##class(%Device).ReDirectIO(1)

        Do ##class(%ZEN.Auxiliary.jsonProvider).%ObjectToJSON(Object)
    } Catch Ex {
        Set Str = ""
    }

    //Return to original redirection/mnemonic routine settings
    If (OldMnemonic '= "") {
        Use OldIO::("^"_OldMnemonic)
    } Else {
        Use OldIO
    }
    Do ##class(%Device).ReDirectIO(OldIORedirected)

    Quit Str

    // Labels that allow for IO redirection
    // Read Character - we don't care about reading
rchr(c)      Quit
    // Read a string - we don't care about reading
rstr(sz,to)  Quit
    // Write a character - call the output label
wchr(s)      Do output($char(s))  Quit
    // Write a form feed - call the output label
wff()        Do output($char(12))  Quit
    // Write a newline - call the output label
wnl()        Do output($char(13,10))  Quit
    // Write a string - call the output label
wstr(s)      Do output(s)  Quit
    // Write a tab - call the output label
wtab(s)      Do output($char(9))  Quit
    // Output label - this is where you would handle what you actually want to do.
    // in our case, we want to write to Str
output(s)    Set Str = Str_s Quit
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A log entry with an object-type argument looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--68jyDwRY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/snimok_18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--68jyDwRY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/snimok_18.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Restoring the context&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The idea of this method is to make all arguments available in the current context (mostly in the terminal, for debugging). To this end, we can use the ProcedureBlock method parameter. When set to 0, all variables declared within such a method will remain available upon quitting the method. Our method will open an object of the App.Log class and deserialize the Arguments property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod LoadContext(Id) As %Status [ ProcedureBlock = 0 ]
{
    Return:'..%ExistsId(Id) $$$OK
    Set Obj = ..%OpenId(Id)
    Set Arguments = Obj.Arguments
    Set List = ..GetMethodArgumentsList(Obj.ClassName,Obj.MethodName)
    For i=1:1:$Length(Arguments,";")-1 {
        Set Argument = $Piece(Arguments,";",i)
        Set @$lg($lg(List,i)) = ..DeserializeObject($Piece(Argument,"=",2),$lg($lg(List,i),2))
    }
    Kill Obj,Arguments,Argument,i,Id,List
}

ClassMethod DeserializeObject(String, ClassName) As %String
{
    If $ClassMethod(ClassName, "%Extends", "%RegisteredObject") {
        // it's an object
        Set st = ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(String,,.obj)
        Return:$$$ISOK(st) obj
    }
    Return String
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how it looks in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;zw
&amp;gt;do ##class(App.Log).LoadContext(2)
&amp;gt;zw

a=1
b=&amp;lt;OBJECT REFERENCE&amp;gt;[2@%ZEN.proxyObject]

&amp;gt;zw b
b=&amp;lt;OBJECT REFERENCE&amp;gt;[2@%ZEN.proxyObject]
+----------------- general information ---------------
|      oref value: 2
|      class name: %ZEN.proxyObject
| reference count: 2
+----------------- attribute values ------------------
|           %changed = 1
|     %data("prop1") = 123
|     %data("prop2") = "abc"
|             %index = ""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's next?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The key potential improvement  is to add another argument to the log class with an arbitrary list of variables created inside the method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Macros can be quite useful for application development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Is there a way to obtain line number during compilation?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://community.intersystems.com/post/macros-intersystems-cach%C3%A9"&gt;Part I. Macros&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/intersystems-ru/Log"&gt;GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Developing REST API with a spec-first approach in InterSystems IRIS</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Sat, 09 Jan 2021 10:19:54 +0000</pubDate>
      <link>https://dev.to/intersystems/developing-rest-api-with-a-spec-first-approach-in-intersystems-iris-4jbk</link>
      <guid>https://dev.to/intersystems/developing-rest-api-with-a-spec-first-approach-in-intersystems-iris-4jbk</guid>
      <description>&lt;p&gt;In this article, I would like to talk about the spec-first approach to REST API development.&lt;/p&gt;

&lt;p&gt;While traditional code-first REST API development goes like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing code&lt;/li&gt;
&lt;li&gt;REST-enabling it&lt;/li&gt;
&lt;li&gt;Documenting it (as a REST API)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spec-first follows the same steps but reverse. We start with a spec, also doubling as documentation, generate a boilerplate REST app from that and finally write some business logic.&lt;/p&gt;

&lt;p&gt;This is advantageous because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You always have relevant and useful documentation for external or frontend developers who want to use your REST API&lt;/li&gt;
&lt;li&gt;Specification created in OAS (Swagger) can be imported into a variety of tools allowing editing, client generation, API Management, Unit Testing and automation or simplification of many other tasks&lt;/li&gt;
&lt;li&gt;Improved API architecture.  In code-first approach, API is developed method by method so a developer can easily lose track of the overall API  architecture, however with the spec-first developer is forced to interact with an API from the position if API consumer which usually helps with designing cleaner API architecture&lt;/li&gt;
&lt;li&gt;Faster development - as all boilerplate code is automatically generated you won't have to write it, all that's left is developing business logic.&lt;/li&gt;
&lt;li&gt;Faster feedback loops - consumers can get a view of the API immediately and they can easier offer suggestions simply by modifying the spec&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's develop our API in a spec-first approach! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plan&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Develop spec in swagger

&lt;ul&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Locally&lt;/li&gt;
&lt;li&gt;Online&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Load spec into IRIS

&lt;ul&gt;
&lt;li&gt;API Management REST API&lt;/li&gt;
&lt;li&gt;^REST&lt;/li&gt;
&lt;li&gt;Classes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;What happened with our spec?&lt;/li&gt;
&lt;li&gt;Implementation&lt;/li&gt;
&lt;li&gt;Further development&lt;/li&gt;
&lt;li&gt;Considerations

&lt;ul&gt;
&lt;li&gt;Special parameters&lt;/li&gt;
&lt;li&gt;CORS&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Load spec into IAM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Develop specification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first step is unsurprisingly writing the spec. InterSystems IRIS supports Open API Specification (from &lt;a href="https://swagger.io/docs/specification/about/"&gt;Swagger&lt;/a&gt; docs):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;OpenAPI Specification&lt;/strong&gt; (formerly Swagger Specification) is an API description format for REST APIs. An OpenAPI file allows you to describe your entire API, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Available endpoints (&lt;code&gt;/users&lt;/code&gt;) and operations on each endpoint (&lt;code&gt;GET /users&lt;/code&gt;, &lt;code&gt;POST /users&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Operation parameters Input and output for each operation&lt;/li&gt;
&lt;li&gt;Authentication methods&lt;/li&gt;
&lt;li&gt;Contact information, license, terms of use and other information.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;API specifications can be written in YAML or JSON. The format is easy to learn and readable to both humans and machines. The complete OpenAPI Specification can be found on GitHub: &lt;a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md"&gt;OpenAPI 3.0 Specification&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will use Swagger to write our API. There are several ways to use Swagger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://editor.swagger.io/"&gt;Online&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Docker: &lt;code&gt;docker run -d -p 8080:8080 swaggerapi/swagger-editor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://swagger.io/docs/open-source-tools/swagger-editor/"&gt;Local installation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After installing/running Swagger, you should see this window in a web browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uVTo65m6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191118205553-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uVTo65m6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191118205553-1.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the left side, you edit the API specification and on the right, you immediately see rendered API documentation/testing tool.&lt;/p&gt;

&lt;p&gt;Let's load our first API spec into it (in &lt;a href="https://en.wikipedia.org/wiki/YAML"&gt;YAML&lt;/a&gt;). It is a simple API with one GET request - returning random number in a specified range.&lt;/p&gt;

&lt;p&gt;Math API Specification&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;swagger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.0"&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Math"&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0"&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Math&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;REST&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API"&lt;/span&gt;
&lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost:52773"&lt;/span&gt;
&lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/math"&lt;/span&gt;
&lt;span class="na"&gt;schemes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/random/{min}/{max}&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;x-ISC_CORS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;random&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;integer"&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;random&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;integer&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;between&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;max"&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;getRandom"&lt;/span&gt;
      &lt;span class="na"&gt;produces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json"&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min"&lt;/span&gt;
        &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path"&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Minimal&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Integer"&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer"&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int32"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max"&lt;/span&gt;
        &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path"&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Maximal&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Integer"&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer"&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int32"&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;200&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OK"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what it consists of.&lt;/p&gt;

&lt;p&gt;Basic information about our API and used OAS version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;swagger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2.0"&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Math"&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0.0"&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Math&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;REST&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Server host, protocol (http, https) and Web application names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost:52773"&lt;/span&gt;
&lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/math"&lt;/span&gt;
&lt;span class="na"&gt;schemes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we specify a path (so complete URL would be &lt;a href="http://localhost:52773/math/random/:min/:max"&gt;http://localhost:52773/math/random/:min/:max&lt;/a&gt;) and HTTP request method (get, post, put, delete):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/random/{min}/{max}&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, we specify information about our request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="na"&gt;x-ISC_CORS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;random&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;integer"&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;random&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;integer&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;between&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;min&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;max"&lt;/span&gt;
      &lt;span class="na"&gt;operationId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;getRandom"&lt;/span&gt;
      &lt;span class="na"&gt;produces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json"&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min"&lt;/span&gt;
        &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path"&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Minimal&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Integer"&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer"&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int32"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max"&lt;/span&gt;
        &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path"&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Maximal&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Integer"&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer"&lt;/span&gt;
        &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int32"&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;200&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OK"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this part we define our request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable this path for CORS (more on that later)&lt;/li&gt;
&lt;li&gt;Provide &lt;em&gt;summary&lt;/em&gt; and &lt;em&gt;description&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;operationId&lt;/em&gt; allows in-spec reference, also it's a generated method name in our implementation class&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;produces&lt;/em&gt; - response format (such as text, xml, json)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;parameters&lt;/em&gt; specify input parameters (be they in URL or body), in our case we specify 2 parameters - range for our random number generator&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;responses&lt;/em&gt; list possible responses form server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you see this format is not particularly challenging, although there are many more features available, here's a &lt;a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md"&gt;specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, let's export our definition as a JSON. Go To File → Convert and save as JSON. The specification should look like this:&lt;/p&gt;

&lt;p&gt;Math API Specification&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"swagger"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Math"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Math REST API"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"localhost:52773"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"basePath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/math"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"schemes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"/random/{min}/{max}"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"x-ISC_CORS"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Get random integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Get random integer between min and max"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"operationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"getRandom"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"produces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"min"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Minimal Integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"int32"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"max"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Maximal Integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"int32"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"200"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Load specification into IRIS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our spec, we can generate boilerplate code for this REST API in InterSystems IRIS.&lt;/p&gt;

&lt;p&gt;To move to this stage we'll need three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST Application name: package for our generated code (let's say math)&lt;/li&gt;
&lt;li&gt;OAS spec in a JSON format: we just created it in a previous step&lt;/li&gt;
&lt;li&gt;WEB Application name: a base path to access our REST API (/math in our case)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are three ways to use our spec for code generation, they are essentially the same and just offer various ways to access the same functionality&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;^%REST&lt;/code&gt; routine (&lt;code&gt;Do ^%REST&lt;/code&gt; in an interactive terminal session), &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GREST_routine"&gt;documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;%REST&lt;/code&gt; class (&lt;code&gt;Set sc = ##class(%REST.API).CreateApplication(applicationName, spec)&lt;/code&gt;, non-interactive), &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_objectscriptapi"&gt;documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use API Management REST API, &lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_apimgmnt"&gt;documentation&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I think documentation adequately describes required steps so just choose one. I'll add two notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In case (1) and (2) you can pass a dynamic object a filename or a URL&lt;/li&gt;
&lt;li&gt;In cases (2) and (3) you &lt;strong&gt;must&lt;/strong&gt; make an additional call to create a WEB application: &lt;code&gt;set sc = ##class(%SYS.REST).DeployApplication(restApp, webApp, authenticationType)&lt;/code&gt;, so in our case &lt;code&gt;set sc = ##class(%SYS.REST).DeployApplication(&amp;amp;quot;math&amp;amp;quot;, &amp;amp;quot;/math&amp;amp;quot;)&lt;/code&gt;, get values for authenticationType argument from %sySecurity include file, relevant entries are &lt;code&gt;$$$Authe\*&lt;/code&gt;, so for unauthenticated access pass &lt;code&gt;$$$AutheUnauthenticated&lt;/code&gt;. If omitted, the parameter defaults to password authentication.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What happened with our spec?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you've created the app successfully, new math package should be created with three classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Spec&lt;/em&gt; - stores the specification as-is.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Disp&lt;/em&gt; - directly called when the REST service is invoked. It wraps REST handling and calls implementation methods.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Impl&lt;/em&gt; - holds the actual internal implementation of the REST service. You should edit only this class.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_intro#GREST_intro_classes"&gt;Documentation&lt;/a&gt;with more information about the classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Initially our implementation class math.impl contains only one method, corresponding to our /random/{min}/{max} operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// Get random integer between min and max&amp;lt;br/&amp;gt;
/// The method arguments hold values for:&amp;lt;br/&amp;gt;
///     min, Minimal Integer&amp;lt;br/&amp;gt;
///     max, Maximal Integer&amp;lt;br/&amp;gt;
ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject
{
    //(Place business logic here)
    //Do ..%SetStatusCode(&amp;lt;HTTP_status_code&amp;gt;)
    //Do ..%SetHeader(&amp;lt;name&amp;gt;,&amp;lt;value&amp;gt;)
    //Quit (Place response here) ; response may be a string, stream or dynamic object
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's start with the trivial implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject
{
    quit {"value":($random(max-min)+min)}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally we can call our REST API by opening this page in browser : &lt;a href="http://localhost:52773/math/random/1/100"&gt;http://localhost:52773/math/random/1/100&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output should be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also in the Swagger editor pressing &lt;code&gt;Try it out&lt;/code&gt; button and filling the request parameters would also send the same request:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O8ZLgu-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120141939-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O8ZLgu-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120141939-1.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! Our first REST API created with a spec-first approach is now live!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Of course, our API is not static and we need to add new paths and so on. With spec-first development, you start with modifying the specification, then updating the REST application (same calls as for creating the application) and finally writing the code. Note that spec updates are safe: your code is not affected, even if the path is removed from a spec, in implementation class the method would not be deleted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Considerations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;More notes!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Special parameters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;InterSystems added special parameters to swagger specification, here they are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Datatype&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Place&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;x-ISC_DispatchParent&lt;/td&gt;
&lt;td&gt;classname&lt;/td&gt;
&lt;td&gt;%CSP.REST&lt;/td&gt;
&lt;td&gt;info&lt;/td&gt;
&lt;td&gt;Superclass for dispatch class.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x-ISC_CORS&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;operation&lt;/td&gt;
&lt;td&gt;Flag to indicate that CORS requests for this endpoint/method combination should be supported.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x-ISC_RequiredResource&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;operation&lt;/td&gt;
&lt;td&gt;Comma-separated list of defined resources and their access modes (resource:mode) that are required for access to this endpoint of the REST service. Example: ["%Development:USE"]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x-ISC_ServiceMethod&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;operation&lt;/td&gt;
&lt;td&gt;Name of the class method called on the back end to service this operation; default is operationId, which is normally suitable.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;CORS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are three ways to enable CORS support.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;On a route by route basis by specifying &lt;code&gt;x-ISC\_CORS&lt;/code&gt; as true. That's what we have done in our Math REST API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On per API basis by adding&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Parameter HandleCorsRequest = 1;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and recompiling the class. It would also survive spec update.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;(Recommended) On per API basis by implementing custom dispatcher superclass (it should extend &lt;code&gt;%CSP.REST&lt;/code&gt;) and writing CORS processing logic there. To use this superclass add &lt;code&gt;x-ISC\_DispatchParent&lt;/code&gt; to your specification.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Load spec into IAM&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finally, let's add our spec into IAM so it would be published for other Developers.&lt;/p&gt;

&lt;p&gt;If you have not started with IAM, check out&lt;a href="https://community.intersystems.com/post/introducing-intersystems-api-manager"&gt;this article&lt;/a&gt;. It also covers offering REST API via IAM so I'm not describing it here. You might want to modify spec host and basepath parameters so that they point to IAM, rather than the InterSystems IRIS instance.&lt;/p&gt;

&lt;p&gt;Open the IAM Administrator portal and go to the Specs tab in the relevant workspace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iG_Zya-Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120150407-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iG_Zya-Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120150407-1.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the Add Spec button and input the name of the new API (math in our case). After creating new Spec in IAM click Edit and paste the spec code (JSON or YAML - it doesn't matter for IAM):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iJezhJx1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120151502-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iJezhJx1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120151502-2.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't forget to click &lt;code&gt;Update File&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now our API is published for Developers. Open Developer Portal and click Documentation in the upper right corner. In addition to the three default APIs our new Math REST API should be available:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jWys8NKT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120151654-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jWys8NKT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120151654-3.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WRyR3n8Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120151813-4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WRyR3n8Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://community.intersystems.com/sites/default/files/inline/images/images/image-20191120151813-4.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now Developers can see the documentation for our new API and try it at the same place!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;InterSystems IRIS simplifies the development process for a REST API and the spec-first approach allows faster and easier REST API life cycle management. With this approach, you can use a variety of tools for a variety of related tasks, such as client generation, unit testing, API Management, and many others.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md"&gt;OpenAPI 3.0 Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST"&gt;Creating REST Services&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://community.intersystems.com/post/introducing-intersystems-api-manager"&gt;Starting with IAM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.intersystems.com/irislatest/csp/docbook/apimgr/index.html"&gt;IAM Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Generate Swagger spec from persistent and serial classes in InterSystems IRIS</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Mon, 28 Dec 2020 14:21:03 +0000</pubDate>
      <link>https://dev.to/intersystems/generate-swagger-spec-from-persistent-and-serial-classes-in-intersystems-iris-4246</link>
      <guid>https://dev.to/intersystems/generate-swagger-spec-from-persistent-and-serial-classes-in-intersystems-iris-4246</guid>
      <description>&lt;p&gt;Recently I needed to generate a Swagger spec from persistent and serial classes, so I'm publishing my code (it's not complete - you still need to hash out the application specifics, but it's a start). It's available &lt;a href="https://github.com/eduard93/Utils/blob/master/Utils/YAML.cls.xml"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's say you have these classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class REST.Test.Person Extends %Persistent
{

/// Person's name.
Property Name As %String [ Required ];

/// Person's Social Security number. This is validated using pattern match.
Property SSN As %String [ Required ];

/// Person's Date of Birth.
Property DOB As %Date;

/// Person's home address. This uses an embedded object.
Property Home As Address;

/// Person's office address. This uses an embedded object.
Property Office As Address;

/// Person's spouse. This is a reference to another persistent object.
Property Spouse As Person;

/// A collection of strings representing the person's favorite colors.
Property FavoriteColors As list Of %String;

/// A collection of strings representing the person's favorite colors.
Property FavoriteNumbers As array Of %Integer;

/// Person's age.&amp;lt;br&amp;gt;
/// This is a calculated field whose value is derived from &amp;lt;property&amp;gt;DOB&amp;lt;/property&amp;gt;.
Property Age As %Integer;

}

Class REST.Test.Address Extends %SerialObject
{

/// The street address.
Property Street As %String(MAXLEN = 80);

/// The city name.
Property City As %String(MAXLEN = 80);

/// The 2-letter state abbreviation.
Property State As %String(MAXLEN = 2);

/// The 5-digit U.S. Zone Improvement Plan (ZIP) code.
Property Zip As %String(MAXLEN = 5);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can automatically generate this Swagger definition from them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="s"&gt;REST.Test.Person&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object"&lt;/span&gt;
   &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;Age&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;integer"&lt;/span&gt;
     &lt;span class="na"&gt;DOB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;
     &lt;span class="na"&gt;FavoriteColors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;array"&lt;/span&gt;
       &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;
     &lt;span class="na"&gt;FavoriteNumbers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object"&lt;/span&gt;
     &lt;span class="na"&gt;Home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="s"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/definitions/REST.Test.Address"&lt;/span&gt;
     &lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;
     &lt;span class="na"&gt;Office&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="s"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/definitions/REST.Test.Address"&lt;/span&gt;
     &lt;span class="na"&gt;SSN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;
     &lt;span class="na"&gt;Spouse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="s"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/definitions/REST.Test.Person"&lt;/span&gt;
 &lt;span class="s"&gt;REST.Test.Address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object"&lt;/span&gt;
   &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;City&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;
     &lt;span class="na"&gt;State&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;
     &lt;span class="na"&gt;Street&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;
     &lt;span class="na"&gt;Zip&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Main method: &lt;code&gt;Utils.YAML:GenerateClasses&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Test run: &lt;code&gt;do ##class(Utils.YAML).Test()&lt;/code&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Community Python Gateway for InterSystems IRIS: Introduction
</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Thu, 03 Dec 2020 09:02:46 +0000</pubDate>
      <link>https://dev.to/intersystems/community-python-gateway-for-intersystems-iris-introduction-27h6</link>
      <guid>https://dev.to/intersystems/community-python-gateway-for-intersystems-iris-introduction-27h6</guid>
      <description>&lt;p&gt;This series of articles would cover community project &lt;a href="https://openexchange.intersystems.com/package/PythonGateway"&gt;Python Gateway&lt;/a&gt; for InterSystems Data Platforms. Execute Python code and more from InterSystems IRIS. This project brings you the power of Python right into your InterSystems IRIS environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Execute arbitrary Python code&lt;/li&gt;
&lt;li&gt;Seamlessly transfer data from InterSystems IRIS into Python&lt;/li&gt;
&lt;li&gt;Build intelligent Interoperability business processes with Python Interoperability Adapter&lt;/li&gt;
&lt;li&gt;Save, examine, modify and restore Python context from InterSystems IRIS&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Other articles
&lt;/h1&gt;

&lt;p&gt;The plan for the series so far (subject to change).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part I: Overview, Landscape and Introduction &amp;lt;-- you're here&lt;/li&gt;
&lt;li&gt;Part II: Installation and Troubleshooting&lt;/li&gt;
&lt;li&gt;Part III: Basic functionality&lt;/li&gt;
&lt;li&gt;Part IV: Interoperability Adapter&lt;/li&gt;
&lt;li&gt;Part V: Execute function&lt;/li&gt;
&lt;li&gt;Part VI: Dynamic Gateway&lt;/li&gt;
&lt;li&gt;Part VII: Proxy Gateway&lt;/li&gt;
&lt;li&gt;Part VIII: Use cases and ML Toolkit&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;Machine learning (ML) - is the study of algorithms and statistical models to effectively perform a specific task without using explicit instructions, relying on patterns and inference instead.&lt;/p&gt;

&lt;p&gt;Machine learning algorithms and models are becoming more and more commonplace. There is a &lt;a href="https://community.intersystems.com/post/should-we-use-computers"&gt;variety of reasons&lt;/a&gt; for that, but it all comes down to affordability, simplicity and producing actionable results. Is clustering or even neural network modeling a new technology? Of course not, but nowadays you do not need to write hundreds of thousands lines of code to run one and the costs are much more manageable.&lt;/p&gt;

&lt;p&gt;Tools are evolving - while we currently do not have completely GUI-based AI/ML tools, but the same progress we saw with many other computer technologies, most notable being BI tools (from writing code to utilizing frameworks to GUI-based configurable solutions) is seen with AI/ML tools. We already passed the point of writing code and are currently utilizing frameworks to configure and calculate the models. &lt;/p&gt;

&lt;p&gt;Other improvements, i.e. distributing pre-trained model, where end user should just finish model training on a real-life data also simplify onboarding process. These advances make getting into data science a much easier endeavor for both individuals and companies.&lt;/p&gt;

&lt;p&gt;On the other hand nowadays we collect more data about every transaction business makes. With a unified data platform such as InterSystems IRIS all this information can be accessed immediately and used as a fuel for predictive models.&lt;/p&gt;

&lt;p&gt;With the other big mover – cloud, running AI/ML workloads becomes easier than ever. Even more important is that we can consume only the resources we require. Moreover, with massive parallelization offered by cloud platforms we can save on a time to a working solution.&lt;/p&gt;

&lt;p&gt;But what about results? Here it gets a little trickier. There are lots of tools to build a model, and I’ll talk about them later, and it’s not always easy to build a good model, but what comes after? Extracting business value from a model is also a nontrivial endeavor. The root of the problem is the separation of analytical and transactional data flows and data models. When we train the model, we usually do that on a historical data in a warehouse system. But the greatest place for the built model to be is in the heart of transactional processing. What good is the best fraud detection model if we run it once a day? The criminals would be long gone with the money. We need to train the model on a historical data but we also need to apply the model in a real time on the new incoming data so that our business processes can act on predictions the model makes.&lt;/p&gt;

&lt;h1&gt;
  
  
  MLToolkit
&lt;/h1&gt;

&lt;p&gt;MLToolkit is a comprehensive set of tools, which aims to do exactly that – bring predictive models and transactional environments together, so that the models you build can be easily leveraged right inside your business processes. Titular Python Gateway is a part of MLToolkit and provides integration with the Python language.&lt;/p&gt;

&lt;h1&gt;
  
  
  Landscape
&lt;/h1&gt;

&lt;p&gt;Before we go further, I would like to describe several tools and libraries for Python, which we would use later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python is an interpreted, high-level, general-purpose programming language. The main advantage of the language is a big library of mathematical, ML and AI libraries. Same as ObjectScript it's an object-oriented language but everything is dynamic rather that static. Also, everything is an object. The later articles assume a passing familiarity with the language. If you want to start learning, I recommend &lt;a href="https://docs.python.org/3.6/tutorial/index.html"&gt;starting with documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For our later exercises &lt;a href="https://www.python.org/downloads/release/python-367/"&gt;install Python 3.6.7 64 bit&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;IDE: I use &lt;a href="https://www.jetbrains.com/pycharm/"&gt;PyCharm&lt;/a&gt;, bet there are a &lt;a href="https://realpython.com/python-ides-code-editors-guide/"&gt;lot of them&lt;/a&gt;. If you're using Atelier, Eclipse for Python developers is a thing.&lt;/li&gt;
&lt;li&gt;Notebook: instead of IDE you can write and share your scripts in a Web-based notebook. The most popular one is &lt;a href="https://jupyter.org/"&gt;Jupyter&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Libraries
&lt;/h2&gt;

&lt;p&gt;Here's a (incomplete) list of libraries used for Machine Learning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.numpy.org/"&gt;Numpy&lt;/a&gt; is the fundamental package for scientific computing with Python.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://pandas.pydata.org/"&gt;Pandas&lt;/a&gt; is a library providing high-performance, easy-to-use data structures and data analysis tools.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://matplotlib.org/"&gt;Matplotlib&lt;/a&gt; is a 2D plotting library which produces figures in a variety of hardcopy formats and interactive environments across platforms.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://seaborn.pydata.org/"&gt;Seaborn&lt;/a&gt; is a data visualization library based on matplotlib. It provides a high-level interface for drawing attractive and informative statistical graphics.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://scikit-learn.org/stable/"&gt;Sklearn&lt;/a&gt; is a machine Learning library.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://xgboost.readthedocs.io/en/latest/index.html"&gt;XGBoost&lt;/a&gt; is an optimized distributed gradient boosting library designed to be highly efficient, flexible and portable. It implements machine learning algorithms under the Gradient Boosting framework.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://radimrehurek.com/gensim/"&gt;Gensim&lt;/a&gt; is a library for unsupervised topic modeling and natural language processing.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://keras.io/"&gt;Keras&lt;/a&gt; is a high-level neural networks API, written in Python and capable of running on top of TensorFlow, CNTK, or Theano.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.tensorflow.org/"&gt;Tensorflow&lt;/a&gt; is an end-to-end open source machine learning platform.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pytorch.org/"&gt;PyTorch&lt;/a&gt; deep learning platform similar to Tensorflow but Python focused.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/nyoka-pmml/nyoka"&gt;Nyoka&lt;/a&gt; produces PMML from Python models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;AI/ML technologies allow business to be more effective and more adaptable. Moreover, today these technologies are becoming easier to build and deploy. Start investigating AI/ML technologies and how it can help your organization to grow and prosper. There are examples, stories and use cases from almost every industry. Do not miss your chance to use future technologies today.&lt;/p&gt;

&lt;h1&gt;
  
  
  What's next
&lt;/h1&gt;

&lt;p&gt;In the next part we would install Python Gateway.&lt;/p&gt;

&lt;h1&gt;
  
  
  Links
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://openexchange.intersystems.com/package/PythonGateway"&gt;Python Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.python.org/downloads/release/python-367/"&gt;Install Python 3.6.7 64 bit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3.6/tutorial/index.html"&gt;Python documentation/tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>InterSystems IRIS - Iterate over dynamic object</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Fri, 13 Nov 2020 13:11:39 +0000</pubDate>
      <link>https://dev.to/intersystems/intersystems-iris-iterate-over-dynamic-object-1kjo</link>
      <guid>https://dev.to/intersystems/intersystems-iris-iterate-over-dynamic-object-1kjo</guid>
      <description>&lt;p&gt;Here's a sample code to display JSON or dynamic object.&lt;/p&gt;

&lt;p&gt;It shows how to iterate over object, get property values and their paths.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class JSON.Test
{

/// do ##class(JSON.Test).Test()
ClassMethod Test()
{
    set json = "{""a"":1,""b"":2,""c"":{""c1"":3,""c2"":4}, ""d"": [5, {""e_e"":6}, 7]}"

    set obj = {}.%FromJSON(json)

    do ..Iterate(obj)
}

ClassMethod Iterate(obj As %DynamicAbstractObject, level = 0, path = "obj")
{
    set indent = $j("", level * 4)
    #dim iterator As %Iterator.Array
    set iterator = obj.%GetIterator()

    while iterator.%GetNext(.key, .value) {
        set type = obj.%GetTypeOf(key)
        write indent, "Key: ", key, !
        write indent, "Type: ", type, !

        if $classname(obj) = "%Library.DynamicArray" {
            set newPath = path _ ".%GetAt(" _ key _ ")"
        } else {
            if $zname(key, 6) = 1 {
                set newPath = path _  "." _ key
            } else {
                set newPath = path _  ".""" _ key _ """"
            }
        }

        write indent, "Path: ", newPath, !
        if $isObject(value) {
            write indent, "Value: ", !
            do ..Iterate(value, level + 1, newPath)
        } else {
            write indent, "Value: ", value, !
        }
        write !
    }
}

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output from running Test method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Key: a
Type: number
Path: obj.a
Value: 1

Key: b
Type: number
Path: obj.b
Value: 2

Key: c
Type: object
Path: obj.c
Value:
    Key: c1
    Type: number
    Path: obj.c.c1
    Value: 3

    Key: c2
    Type: number
    Path: obj.c.c2
    Value: 4


Key: d
Type: array
Path: obj.d
Value:
    Key: 0
    Type: number
    Path: obj.d.%GetAt(0)
    Value: 5

    Key: 1
    Type: object
    Path: obj.d.%GetAt(1)
    Value:
        Key: e_e
        Type: number
        Path: obj.d.%GetAt(1)."e_e"
        Value: 6


    Key: 2
    Type: number
    Path: obj.d.%GetAt(2)
    Value: 7

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>intersystems</category>
      <category>intersystemsiris</category>
      <category>json</category>
    </item>
    <item>
      <title>Adding your own provider to MFT</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Mon, 19 Oct 2020 16:22:08 +0000</pubDate>
      <link>https://dev.to/intersystems/adding-your-own-provider-to-mft-2407</link>
      <guid>https://dev.to/intersystems/adding-your-own-provider-to-mft-2407</guid>
      <description>&lt;p&gt;&lt;a href="http://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_mft"&gt;Managed File Transfer&lt;/a&gt; (MFT) feature of InterSystems IRIS enables easy inclusion of a third-party file transfer service directly into an InterSystems IRIS production. Currently, DropBox, Box, and Kiteworks cloud disks are available.&lt;/p&gt;

&lt;p&gt;In this article, I'd like to describe how to add more cloud storage platforms.&lt;/p&gt;

&lt;p&gt;Here's what we're going to talk about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  What is MFT&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Reference: Dropbox&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Connection&lt;/li&gt;
&lt;li&gt;  Interoperability&lt;/li&gt;
&lt;li&gt;  Direct access&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Interfaces you need to implement&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Connection&lt;/li&gt;
&lt;li&gt;  Logic&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Installation&lt;a href="https://practicaldev-herokuapp-com.freetls.fastly.net/sites/all/modules/contrib/wysiwyg/plugins/break/images/spacer.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/sites/all/modules/contrib/wysiwyg/plugins/break/images/spacer.gif" alt="&amp;lt;--break-&amp;gt;" title="&amp;lt;--break--&amp;gt;"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is MFT
&lt;/h3&gt;

&lt;p&gt;MFT provides bidirectional file transfer, you can both download files from a cloud storage and upload files there. &lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://learning.intersystems.com/course/view.php?id=735"&gt;this video on Learning.InterSystems.com&lt;/a&gt; for an introduction to MFT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference: Dropbox
&lt;/h2&gt;

&lt;p&gt;Before we start writing our own MFT adapter, let's run an existing one. I chose Dropbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic access
&lt;/h3&gt;

&lt;p&gt;First of all, we need to configure Dropbox access:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Register new &lt;a href="https://www.dropbox.com/"&gt;Dropbox account&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt; Create new &lt;a href="https://www.dropbox.com/developers/apps/create"&gt;Dropbox App.&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;  Remember your: &lt;code&gt;App Key&lt;/code&gt; and &lt;code&gt;App Secret&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Set your Redirect URL, probably:  &lt;code&gt;http://localhost:57772/csp/sys/oauth2/OAuth2.Response.cls&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Create a new SSL Configuration (if you don't have one)&lt;/li&gt;
&lt;li&gt; Create a new MFT configuration at: &lt;code&gt;http://localhost:57772/csp/sys/sec/%25CSP.UI.Portal.MFT.Connection.zen?isNew=1&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;  Remove "Use SSL" flag (unless your web server is available over https).&lt;/li&gt;
&lt;li&gt;  Email address field should match your Dropbox email address.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Press &lt;code&gt;Get Access Token&lt;/code&gt; button (on MFT connections list page) to authorize your application.&lt;/li&gt;
&lt;li&gt; If everything went alright, the status of the new MFT connection should be Authorized.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Interoperability
&lt;/h3&gt;

&lt;p&gt;Next, we need to configure Interoperability production to use the new connection&lt;/p&gt;

&lt;p&gt;To receive files from Dropbox into a local directory:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Create &lt;code&gt;EnsLib.MFT.Service.Passthrough&lt;/code&gt; service.

&lt;ul&gt;
&lt;li&gt;  MFT Connection Name: Dropbox&lt;/li&gt;
&lt;li&gt;  MFT Source Folders: the path to Dropbox folder, from which the files would be downloaded (i.e. &lt;code&gt;/inbox/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  Target Config Names: BP or BO to send files to, for example, &lt;code&gt;EnsLib.File.PassthroughOperation&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Create &lt;code&gt;EnsLib.File.PassthroughOperation&lt;/code&gt; operation.

&lt;ul&gt;
&lt;li&gt;  File Path:  the path to a local directory to which the files from Dropbox would be downloaded (i.e. &lt;code&gt;C:\\temp\in\&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  Charset: Binary&lt;/li&gt;
&lt;li&gt;  File Name: %f&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's all. After starting the production, files from the specified Dropbox folder would be downloaded into the local directory.&lt;/p&gt;

&lt;p&gt;To upload files to Dropbox, the process should be done in reverse:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Create  &lt;code&gt;EnsLib.MFT.Operation.Passthrough&lt;/code&gt; service.

&lt;ul&gt;
&lt;li&gt;  MFT Connection Name: Dropbox&lt;/li&gt;
&lt;li&gt;  Default MFT Folder: the path to Dropbox folder, into which the files would be uploaded (i.e. &lt;code&gt;/sent/&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Create &lt;code&gt;EnsLib.File.PassthroughService&lt;/code&gt;operation.

&lt;ul&gt;
&lt;li&gt;  File Path:  the path to a local directory from which the files would be uploaded to Dropbox (i.e. &lt;code&gt;C:\\temp\out\&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  Target Config Names: BP or BO to send files to&lt;code&gt;,&lt;/code&gt; in our case &lt;code&gt;EnsLib.MFT.Operation.Passthrough&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Charset: Binary&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And with this, files placed into &lt;code&gt;C:\\temp\out\&lt;/code&gt; folder would be uploaded into &lt;code&gt;/sent/&lt;/code&gt; Dropbox folder.&lt;/p&gt;

&lt;p&gt;For a more comprehensive guide refer to &lt;a href="http://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_mft"&gt;MFT First look&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  "Direct" access
&lt;/h3&gt;

&lt;p&gt;Interoperability  BS/BO are wrappers over a direct access. When writing your own adapter it's better to use direct access to check if things work as expected. Here's a sample method to get file information&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod dropboxInfo(file = "/1.txt")
{
    // Establishing Dropbox connection
    set mftConnectionName = "Dropbox"
    set mftConnection = ##class(%MFT.API).GetConnection(mftConnectionName, .sc)
    write:$$$ISERR(sc) $System.Status.GetErrorText(sc)

    // Getting information about file
    // Some other methods: GetFileInfo GetFolderInfo CreateFolder DeleteFile GetUser ShareFolder UnshareFolder DownloadStream UploadFile
    set sc = $classmethod($$$EnsCoreMFTAPIClass,"GetFileInfo", mftConnection, file, .itemInfo)
    write:$$$ISERR(sc) $System.Status.GetErrorText(sc)

    // Displaying information about file
    #dim itemInfo As %MFT.ItemInfo
    zw itemInfo
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;--&lt;/p&gt;

&lt;h2&gt;
  
  
  MFT interface
&lt;/h2&gt;

&lt;p&gt;MFT provider consists of two parts. To create your own provider you need to implement them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical API  (connection)
&lt;/h3&gt;

&lt;p&gt;Connection is responsible for sending requests and parsing results. It should extend &lt;code&gt;%SYS.MFT.Connection.Base&lt;/code&gt; and implement the following methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  DefaultURL that returns the root of service API&lt;/li&gt;
&lt;li&gt;  CreateClient that creates OAuth client&lt;/li&gt;
&lt;li&gt;  RevokeToken to log out of the application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other methods could be overloaded as required bit these three are a must. Additionally, for Yandex I created these three methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  MakeRequest - to execute normal API requests&lt;/li&gt;
&lt;li&gt;  MakeDownloadRequest - to download files&lt;/li&gt;
&lt;li&gt;  MakeUploadRequest - to upload files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These methods structure are completely dependent on target API architecture. In my case, a majority of requests were similar, with different requests required for downloading and uploading files so I ended up with three methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logical API
&lt;/h3&gt;

&lt;p&gt;Is responsible for consuming the API, it uses the connection to execute requests and it should extend &lt;code&gt;%MFT.API&lt;/code&gt; and reside in the &lt;code&gt;%MFT&lt;/code&gt; package (we'll talk about working around that requirement later). The methods it must overload could be separated into four categories:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Info&lt;/th&gt;
&lt;th&gt;Create/Delete&lt;/th&gt;
&lt;th&gt;Sharing&lt;/th&gt;
&lt;th&gt;Load&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GetFileInfo   GetFolderInfo   GetFolderContents&lt;/td&gt;
&lt;td&gt;CreateFolder   DeleteFolder   DeleteFile&lt;/td&gt;
&lt;td&gt;ShareFolder   UnshareFolder   UnshareFolderAll&lt;/td&gt;
&lt;td&gt;UploadStream   DownloadStream&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These methods seem self-explanatory. But please ask in comments if the purpose of these methods seems unclear. There's also documentation in %MFT.API class.  I recommend implementing these methods left to right, you can also skip Sharing methods. That brings us to 8 methods that must be implemented.&lt;/p&gt;

&lt;p&gt;Additionally, if your cloud disk provider has user management (i.e. teams and so on), you can manage that too via:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  GetUser&lt;/li&gt;
&lt;li&gt;  GetUserById&lt;/li&gt;
&lt;li&gt;  GetUserList&lt;/li&gt;
&lt;li&gt;  CreateUser&lt;/li&gt;
&lt;li&gt;  DeleteUser&lt;/li&gt;
&lt;li&gt;  DeleteUserById&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As Yandex.Disk does not have user management I skipped these methods (they should still be implemented, but left effectively empty).&lt;/p&gt;

&lt;p&gt;Finally &lt;strong&gt;GetRequestId&lt;/strong&gt; method should be implemented to convert paths into ids (that could be faster), otherwise, it should return path.&lt;/p&gt;

&lt;p&gt;Along with some other methods that are about the same for all providers here's a code snippet you can start writing your adapter with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// Get the form of id for a file or folder that is most efficient for subsequent calls.
/// GetRequestId will return either an id of the form "id:&amp;lt;id&amp;gt;" or a full path depending on which is more efficient.
/// This method is included to allow the id for future requests to be saved in the most efficient form.
ClassMethod GetRequestId(connection As %SYS.MFT.Connection.Base, itemInfo As %MFT.ItemInfo) As %String
{
    Quit itemInfo.GetPath()
}

/// Retrieve the %MFT.UserInfo for current user
ClassMethod GetUser(connection As %SYS.MFT.Connection.Base, username As %String, Output userInfo As %MFT.UserInfo) As %Status
{
    Set userInfo = ##class(%MFT.UserInfo).%New()
    Set userInfo.Username = connection.Username
    Quit sc
}

/// Retrieve the %MFT.UserInfo specified by the service defined Userid.
/// If the user does not exist, then $$$OK is returned as status and userInfo is returned as "".
ClassMethod GetUserById(connection As %SYS.MFT.Connection.Base, userid As %String, Output userInfo As %MFT.UserInfo) As %Status
{
    Quit ..GetUser(connection, userid, .userInfo)
}

/// Return the list of all currently defined users for this team or enterprise.
ClassMethod GetUserList(connection As %SYS.MFT.Connection.Base, Output userList As %MFT.UserList) As %Status
{
    Set sc = $$$OK
    Set userList = ##class(%MFT.UserList).%New()
    Set sc = ..GetUser(connection, "", .userInfo)
    Quit:$$$ISERR(sc) sc

    Do userList.Users.Insert(userInfo)

    Quit sc
}

/// Create a new user.
/// Unable to do it in Yandex
ClassMethod CreateUser(connection As %SYS.MFT.Connection.Base, userInfo As %MFT.UserInfo) As %Status
{
    Quit $$$ERROR($$$MFTBadConnection)
}

/// Delete new user.
/// Unable to do it in Yandex
ClassMethod DeleteUser(connection As %SYS.MFT.Connection.Base, username As %String) As %Status
{
    Quit $$$ERROR($$$MFTBadConnection)
}

/// Delete the user that is specified by the id.
ClassMethod DeleteUserById(connection As %SYS.MFT.Connection.Base, userid As %String) As %Status
{
    Quit $$$ERROR($$$MFTBadConnection)
}

/// Unshare a folder from everyone, user is ignored
ClassMethod UnshareFolder(connection As %SYS.MFT.Connection.Base, path As %String, user As %String) As %Status
{
    Quit ..UnshareFolderAll(connection, path)
}

/// MountFolder is a Dropbox specific method to mount a shared folder that was shared by a different user.
/// MountFolder is treated as a NOP for all other services.
ClassMethod MountFolder(connection As %SYS.MFT.Connection.Box, folderName As %String) As %Status
{
    // A NOP if not Dropbox
    Quit $$$OK
}

/// UnmountFolder is a Dropbox specific method to unmount a shared folder that was shared by a different user.
/// UnmountFolder is treated as a NOP for all other services.
ClassMethod UnmountFolder(connection As %SYS.MFT.Connection.Box, folderName As %String) As %Status
{
    // A NOP if not Dropbox
    Quit $$$OK
}

/// Update the specified remote file with the contents of the specified local file.
/// filePath must be a file path.  An id may not be specified.
/// If replace is true, then an existing file of the same name will be replaced.
/// The default is to return an error if a replacement is attempted.
ClassMethod UploadFile(connection As %SYS.MFT.Connection.Base, localFilePath As %String, filePath As %String, replace As %Boolean = 0, Output itemInfo As %MFT.ItemInfo) As %Status
{
    Set stream=##class(%FileBinaryStream).%New()
    Set stream.Filename=localFilePath
    Quit ..UploadStream(.connection,stream,filePath,replace,.itemInfo)
}

/// Download the specified remote file and store at the location given by localFilePath.
/// filePath may be a file path.
ClassMethod DownloadFile(connection As %SYS.MFT.Connection.Base, filePath As %String, localFilePath As %String) As %Status
{
    Set stream=##class(%FileBinaryStream).%New()
    Set stream.Filename=localFilePath
    Quit ..DownloadStream(.connection,filePath,stream)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Additional data
&lt;/h3&gt;

&lt;p&gt;Some other classes are used to transmit information, they are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;%MFT.ItemInfo&lt;/code&gt; is a detailed description of a file or folder - you'll need it.&lt;/li&gt;
&lt;li&gt;  Other classes are %MFT.UserInfo for user information and  %MFT.FolderContents/%MFT.UserList to list several items or users respectively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note, that these classes, despite being % classes, store the data in the namespace they are called from. They are storing data in ^MFT.* globals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;As our API should be in a %MFT package we would use mapping to map code from the specialized database into our target namespace (or %SYS). That way InterSystems IRIS can be safely updated and our work wouldn't be overwritten. An Interesting thing about it is that we need to load classes only into target namespace - %SYS in our case.&lt;/p&gt;

&lt;p&gt;First of all, we need to download our code, to do that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Download and import &lt;a href="https://raw.githubusercontent.com/intersystems-ru/MFTAdapters/master/MFT/Installer.cls"&gt;Installer&lt;/a&gt; into any Interoperability-enabled namespace.&lt;/li&gt;
&lt;li&gt; Execute: &lt;code&gt;write $System.Status.GetErrorText(##class(MFT.Installer).Install())&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Create &lt;code&gt;MFTLIB&lt;/code&gt; database&lt;/li&gt;
&lt;li&gt;  Add mapping of &lt;code&gt;%MFT.Addons&lt;/code&gt; and &lt;code&gt;%SYS.MFT.Connection.Addons&lt;/code&gt; packages into &lt;code&gt;%SYS&lt;/code&gt; namespace from &lt;code&gt;MFTLIB&lt;/code&gt; database&lt;/li&gt;
&lt;li&gt;  Download the rest of the code from GitHub, correctly importing % and non % classes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, we need to configure Yandex application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Register on Yandex.&lt;/li&gt;
&lt;li&gt; &lt;a href="https://oauth.yandex.ru/client/new"&gt;Create Yandex App&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;  Check &lt;code&gt;Веб-сервисы&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Set Redirect URI: &lt;code&gt;http://Host:Port/csp/sys/oauth2/OAuth2.Response.cls&lt;/code&gt; (https, if UseSSL = 1, for development you can set it to &lt;code&gt;http://localhost:57772/csp/sys/oauth2/OAuth2.Response.cls&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  Give disk access &lt;code&gt;Яндекс.Диск REST API&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Get &lt;code&gt;ID&lt;/code&gt;, &lt;code&gt;Pass&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Execute: &lt;code&gt;write $System.Status.GetErrorText(##class(MFT.Yandex).Install(Login, ID, Pass, Host, Port, UseSSL))&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;  Login - your Yandex email&lt;/li&gt;
&lt;li&gt;  Host, Port - same as the callback&lt;/li&gt;
&lt;li&gt;  UseSSL - use SSL for callback? Your server needs to support https&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Open &lt;code&gt;http://Host:Port/csp/sys/sec/%25CSP.UI.Portal.MFT.ConnectionList.zen&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Press &lt;code&gt;Get Access Token&lt;/code&gt; and complete authorization.&lt;/li&gt;
&lt;li&gt; If everything went fine the Status would be Authorized.&lt;/li&gt;
&lt;li&gt; Execute: &lt;code&gt;write $System.Status.GetErrorText(##class(MFT.Yandex).ConfigureProduction(yandexSource, fileDestination, fileSource, yandexDestination))&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;yandexSource&lt;/code&gt; and &lt;code&gt;fileDestination&lt;/code&gt; - Yandex.Disk folder to download files from, they are stored in a local destination folder.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;fileSource&lt;/code&gt; and &lt;code&gt;yandexDestination&lt;/code&gt; - local folder from which files are uploaded to Yandex.Disk.&lt;/li&gt;
&lt;li&gt;  Important: Yandex.Disk folder names should end with &lt;code&gt;/&lt;/code&gt; (i.e. &lt;code&gt;out&lt;/code&gt; in a disk root would be &lt;code&gt;/out/&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; Open production &lt;code&gt;MFT.Production&lt;/code&gt; and start it.&lt;/li&gt;
&lt;li&gt; Add file(s) to &lt;code&gt;yandexSource&lt;/code&gt; and &lt;code&gt;fileSource&lt;/code&gt; to see how it works.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note, that unlike Dropbox, I'm creating service automatically, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ClassMethod Install(username As %String, clientId As %String, clientSecret As %String, host As %String = "localhost", port As %Integer = {$get(^%SYS("WebServer","Port"), 57772)}, useSSL As %Boolean = {$$$NO})
{
    New $Namespace
    Set $Namespace = "%SYS"

     Do:'##class(Security.SSLConfigs).Exists(..#SSLConfig) ##class(Security.SSLConfigs).Create(..#SSLConfig)

    Set sys = ##class(%SYS.MFT.Connection.Addons.Yandex).%New()
    Set sys.Name = "Yandex"
    Set sys.Service = "Addons.Yandex"
    Set sys.ApplicationName = "Yandex"
    Set sys.SSLConfiguration = ..#SSLConfig
    Set sys.Username = username
    Set sys.URL = ..#URL

    $$$QuitOnError(##class(%SYS.MFT.Connection.Addons.Yandex).CreateClient(sys.Name, ..#SSLConfig, clientId, clientSecret, ,host, port,,useSSL))

    Quit sys.%Save()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the Service value: &lt;code&gt;Addons.Yandex&lt;/code&gt;. Service name, when appended to &lt;code&gt;%MFT.&lt;/code&gt; should yield the name of the logical API class. Since we can't modify &lt;code&gt;%MFT&lt;/code&gt; package directly we can add a subpackage to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;MFT is a useful technology and can be easily extended to support cloud providers you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://github.com/intersystems-ru/MFTAdapters"&gt;Repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://tech.yandex.com/disk/rest/"&gt;Yandex.Disk REST API  reference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="http://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_mft"&gt;MFT First look&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Classes, tables and globals - how it all works? </title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Wed, 08 Jul 2020 15:54:15 +0000</pubDate>
      <link>https://dev.to/intersystems/classes-tables-and-globals-how-it-all-works-1gl9</link>
      <guid>https://dev.to/intersystems/classes-tables-and-globals-how-it-all-works-1gl9</guid>
      <description>&lt;p&gt;When I describe InterSystems IRIS to more technically-minded people, I always start with how it is a multimodel DBMS at its core.&lt;/p&gt;

&lt;p&gt;In my opinion that is its main advantage (on the DBMS side). And the data is stored only once. You just choose the access API you want to use.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want some sort of summary for your data? Use SQL!&lt;/li&gt;
&lt;li&gt;Do you want to work extensively with one record? Use objects!&lt;/li&gt;
&lt;li&gt;Want to access or set one value and you know the key? Use globals!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On first blush it's a nice story - short and concise and it gets the message across, but when people really start working with InterSystems IRIS the questions start. How are classes and tables and globals related? What are they to each other? How's the data really stored?&lt;/p&gt;

&lt;p&gt;In this article I would try to answer these questions and explain what's really going on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part one. Model bias.
&lt;/h2&gt;

&lt;p&gt;People working with data are often biased towards the model they work with.&lt;/p&gt;

&lt;p&gt;Developers think in objects. For them databases and tables are boxes you interact with via CRUD (Create-Read-Update-Delete, preferably over ORM) but the underlying conceptual model is objects (of course it's mainly true for developers in object-oriented languages - so most of us).&lt;/p&gt;

&lt;p&gt;On the other hand, as a consequence of spending so much time in relational DBMSes, DBAs often think of data as tables. Objects are just wrappers over rows in this case.&lt;/p&gt;

&lt;p&gt;And with InterSystems IRIS, a persistent class is also a table, which stores data in global, so some clarification is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part two. An example.
&lt;/h2&gt;

&lt;p&gt;Let's say you created class Point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class try.Point Extends %Persistent [DDLAllowed]
{
    Property X;
    Property Y;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can also create the same class with DDL/SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE Table try.Point (
    X VARCHAR(50),
    Y VARCHAR(50))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After compilation, our new class would have auto-generated a storage structure which maps data that is natively stored in globals to columns (or properties if you're an object-oriented thinker):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Storage Default
{
&amp;lt;Data name="PointDefaultData"&amp;gt;
    &amp;lt;Value name="1"&amp;gt;
        &amp;lt;Value&amp;gt;%%CLASSNAME&amp;lt;/Value&amp;gt;
    &amp;lt;/Value&amp;gt;
    &amp;lt;Value name="2"&amp;gt;
        &amp;lt;Value&amp;gt;X&amp;lt;/Value&amp;gt;
    &amp;lt;/Value&amp;gt;
    &amp;lt;Value name="3"&amp;gt;
        &amp;lt;Value&amp;gt;Y&amp;lt;/Value&amp;gt;
    &amp;lt;/Value&amp;gt;
&amp;lt;/Data&amp;gt;
&amp;lt;DataLocation&amp;gt;^try.PointD&amp;lt;/DataLocation&amp;gt;
&amp;lt;DefaultData&amp;gt;PointDefaultData&amp;lt;/DefaultData&amp;gt;
&amp;lt;IdLocation&amp;gt;^try.PointD&amp;lt;/IdLocation&amp;gt;
&amp;lt;IndexLocation&amp;gt;^try.PointI&amp;lt;/IndexLocation&amp;gt;
&amp;lt;StreamLocation&amp;gt;^try.PointS&amp;lt;/StreamLocation&amp;gt;
&amp;lt;Type&amp;gt;%Library.CacheStorage&amp;lt;/Type&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;What is going on here?&lt;/p&gt;

&lt;p&gt;From the bottom-up (&lt;strong&gt;bold&lt;/strong&gt; words are important, ignore the rest):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type: generated storage type, in our case the default storage for persistent objects&lt;/li&gt;
&lt;li&gt;StreamLocation - global where we store streams&lt;/li&gt;
&lt;li&gt;IndexLocation - global for indices&lt;/li&gt;
&lt;li&gt;IdLocation - global where we store ID autoincremental counter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DefaultData&lt;/strong&gt; - storage XML element which maps global value to columns/properties&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DataLocation&lt;/strong&gt; - global in which to store data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now our "DefaultData" is &lt;code&gt;PointDefaultData&lt;/code&gt; so let's look closer at its structure. Essentially it says that global node has this structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 - %%CLASSNAME&lt;/li&gt;
&lt;li&gt;2 - X&lt;/li&gt;
&lt;li&gt;3 - Y&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we might expect our global to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;^try.PointD(id) = %%CLASSNAME, X, Y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;But if we output our global it would be empty because we didn't add any data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zw ^try.PointD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's add one object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set p = ##class(try.Point).%New()
set p.X = 1
set p.Y = 2
write p.%Save()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And here's our global&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zw ^try.PointD
^try.PointD=1
^try.PointD(1)=$lb("",1,2)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you see our expected structure %%CLASSNAME, X, Y is set with &lt;code&gt;$lb("",1,2)&lt;/code&gt; which corresponds to X and Y properties of our object (%%CLASSNAME is system property, ignore it).&lt;/p&gt;

&lt;p&gt;We can also add a row via SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO try.Point (X, Y) VALUES (3,4)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now our global looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zw ^try.PointD
^try.PointD=2
^try.PointD(1)=$lb("",1,2)
^try.PointD(2)=$lb("",3,4)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So the data we add via objects or SQL are stored in globals according to storage definitions (side note: you can manually modify the storage definition by replacing X and Y in PointDefaultData  - check what happens to the new data!).&lt;/p&gt;

&lt;p&gt;Now, what happens when we want to execute a SQL query?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM try.Point
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It is translated into ObjectScript code that iterates over the &lt;code&gt;^try.PointD&lt;/code&gt; global and populates columns based on the storage definition - the &lt;code&gt;PointDefaultData&lt;/code&gt; part of it precisely.&lt;/p&gt;

&lt;p&gt;Now for modifications. Let's delete all the data from the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DELETE FROM try.Point
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And let's see our global at this point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zw ^try.PointD
^try.PointD=2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that only ID counter is left, so new object/row would have an ID=3. Also our class and table continue to exist.&lt;/p&gt;

&lt;p&gt;But what happens when we run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DROP TABLE try.Point
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It would destroy the table, the class, and delete the global.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zw ^try.PointD
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you followed this example, I hope you now have a better understanding of how  globals, classes and tables integrate and complement each other. Using the right API for the job at hand results in faster, more agile, less buggy development.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
