From 8b8458a690deeaa47c7917ab7b7cd8858c0a5b16 Mon Sep 17 00:00:00 2001 From: phil1436 Date: Tue, 19 Dec 2023 14:40:52 +0100 Subject: [PATCH] update --- .github/workflows/deploy.yml | 1 + .gitignore | 3 +++ src/assets/chapters.json | 1 - 3 files changed, 4 insertions(+), 1 deletion(-) delete mode 100644 src/assets/chapters.json diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d960ef6..8bf13f4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,6 +4,7 @@ on: push: branches: - main + workflow_dispatch: jobs: deploy: diff --git a/.gitignore b/.gitignore index d1e125d..ee93393 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ ideas.md /.vscode git-github-token.sh PUSHTOGIT.bat + + +pushAll.sh diff --git a/src/assets/chapters.json b/src/assets/chapters.json deleted file mode 100644 index cfee4db..0000000 --- a/src/assets/chapters.json +++ /dev/null @@ -1 +0,0 @@ -[{"Title":"Welcome chapter","Language":"english","Author":"test","Description":"This is a test chapter to welcome you to the InterSystems Workshop Organizer and Optimizer Program!","IsPrivate":false,"Pages":[{"Content":"![logo](assets/imgs/InterSystemsWOOPLogo.png){width: 20%}\n\n# Welcome to InterSystems Workshop Organizer and Optimizer Program\n\nThe InterSystems Workshop Organizer and Optimizer Program is a program designed to help you organize and optimize your InterSystems workshop experience. The program is designed to help you get the most out of your workshop experience by providing you with a set of tools and resources to help you organize your workshop experience."},{"Content":"## Create your first workshop\n\nTo create your first workshop, you will need to be logged in. On the homepage you can see a list of all the workshops you have created and a + button to create a new workshop.\n\nThe program will displays you a form to fill in the details of your workshop:\n\n- **Name**: The name of your workshop.\n- **Language**: The language of your workshop.\n- **Password**: The password of your workshop.\n- **Private**: If enabled the workshop is private and only accessible by you.\n- **Description**: A short description of your workshop."},{"Content":"## Edit your workshop\n\nTo edit your workshop, you will need to be logged in. On the homepage you can see a list of all the workshops you have created and a edit button to edit your workshop.Clicking on the edit button you will be navigated to the edit page of your workshop."},{"Content":"## Add your first page\n\nWhen you edit a workshop you can add and remove pages. Lets add a page and have a look at a page...\n\nHow you can see, the page is divided in three parts:\n\n- **Conent**: Here you can write the content of your page. Usually you will create your exercises here.\n- **Hint**: Here you can write a hint for the exercise.\n- **Result**: Here you can write the solution of the exercise.","Hint":"## Hint\n\nThis is a Hint.","Result":"## Result\n\nThis is a result."},{"Content":"## Writing pages\n\nPages are written in Markdown. You can find a Markdown cheatsheet by clicking on the _help_ button.\n\nLets take a look at all the features of the Markdown language..."},{"Content":"### Haedings\n\nYou can create headings by adding a `#` in front of your text. The number of `#` defines the level of the heading.","Result":"~~~markdown\n# Heading 1\n## Heading 2\n### Heading 3\n#### Heading 4\n##### Heading 5\n~~~"},{"Content":"### Text\n\nYou can write text as you would do in any other text editor.","Result":"~~~markdown\nThis is a text.\n~~~"},{"Content":"### Styling your text\n\n- Text can be **bold** by adding `**` in front and after your text.\n- Text can be *italic* by adding `*` in front and after your text.\n- Text can be ~~strikethrough~~ by adding `~~` in front and after your text.","Result":"~~~markdown\n**bold**\n*italic*\n~~strikethrough~~\n~~~"},{"Content":"### Unordered Lists\n\nYou can create lists by adding a `-` in front of your text.","Result":"~~~markdown\n- Item 1\n- Item 2\n- Item 3\n~~~"},{"Content":"### Ordered Lists\n\nYou can create ordered lists by adding a number and a `.` in front of your text.","Result":"~~~markdown\n1. Item 1\n2. Item 2\n3. Item 3\n~~~"},{"Content":"### Links\n\nYou can create links by adding `[` and `]` in front and after your text. The first part is the text of the link and the second part is the link itself.","Result":"~~~markdown\n[Link text](https://www.intersystems.com)\n~~~"},{"Content":"### Images\n\nYou can add images by adding `![` and `]` in front and after your text. The first part is the alt text of the image and the second part is the link to the image.","Result":"~~~markdown\n![Alt text](url){css}\n~~~"},{"Content":"### Code Block\n\nYou can add code by adding ` ``` ` in front and after your text. The first part is the language of the code and the second part is the code itself.","Result":"~~~markdown\n```javascript\nconsole.log(\"Hello World!\");\n```\n~~~"},{"Content":"### Code Window\n\nYou can add code by adding ` ~~~ ` in front and after your text. The first part is the language of the code and the second part is the code itself.","Result":"~~~markdown\n ~~~javascript\n console.log(\"Hello World!\");\n ~~~\n~~~"},{"Content":"## Have fun!\n\nNow you can create your own workshops and have fun!"}]},{"Title":"Visual Studio Code X InterSystems IRIS","Language":"english","Author":"pb","Description":"An introduction to Microsoft's Visual Studio Code and how to use Visual Studio Code with InterSystems ObjectScript.","IsPrivate":false,"Pages":[{"Content":"![Logo](https://raw.githubusercontent.com/phil1436/VSCode-ObjectScript-HowTo/master/imgs/IrisXVscode.png){width:20%}\n\n# Visual Studio Code X InterSystems IRIS\n\nAn introduction to Microsoft's Visual Studio Code and how to use Visual Studio Code with InterSystems ObjectScript.\n\nView the german version **[here](https://github.com/phil1436/VSCode-ObjectScript-HowTo/tree/german)**."},{"Content":"# Getting Started with Visual Studio Code\n\nVisual Studio Code (VSCode) is a lightweight yet powerful source code editor developed by Microsoft for Windows, Linux, and macOS. It comes with built-in support for debugging, source control, and development tasks such as linting and building. In this guide, we will walk through the basics of using Visual Studio Code to write, debug, and manage your code."},{"Content":"## VSCodium\n\nWhen considering which code editor to use, [VSCodium](https://vscodium.com/) emerges as a compelling alternative to Visual Studio Code. Here are a few reasons why you should consider using VSCodium:\n\n1. **Open Source and Transparency**: VSCodium is a community-driven, open-source project that offers a fully-featured code editor without any telemetry or tracking. Unlike VS Code, which is built on Microsoft's open-source platform but includes telemetry and branding, VSCodium respects your privacy and promotes transparency.\n\n2. **Freedom from Vendor Lock-in**: With VSCodium, you have complete control over your code editor. There are no proprietary build systems or extensions that bind you to a specific ecosystem. You can customize and modify VSCodium according to your needs, allowing you the freedom to tailor it to your workflow.\n\n3. **Enhanced Privacy and Security**: VSCodium eliminates any concerns related to data collection and usage. By removing telemetry, crash reporting, and data sharing functionalities, VSCodium ensures your development environment remains private and secure. This is particularly advantageous for individuals or organizations that prioritize data privacy.\n\n4. **Extensive Plugin Ecosystem**: VSCodium is fully compatible with the vast plugin ecosystem of VS Code. It supports the same wide range of extensions and themes, enabling you to leverage the extensive functionality provided by the VS Code community. You can benefit from the rich library of extensions to enhance your coding experience without compromising on privacy.\n\n5. **Regular Updates and Maintenance**: VSCodium keeps pace with the latest updates from the VS Code project. As a result, you can enjoy the latest features, bug fixes, and performance enhancements in a privacy-focused environment. The active community behind VSCodium ensures ongoing maintenance, making it a reliable and up-to-date choice for developers.\n\nUltimately, the decision between VSCodium and Visual Studio Code depends on your priorities. If privacy, transparency, and customization are important to you, VSCodium offers a compelling alternative to VS Code. By embracing the open-source philosophy and focusing on user control, VSCodium provides a trustworthy code editor while still leveraging the extensive plugin ecosystem of its parent project.\n\nHowever, if you decide to use VSCodium, you can still follow this guide. The two editors are nearly identical, and the instructions provided here will work for both.\n\nDownload VSCodium [here](https://vscodium.com/#install)."},{"Content":"## Install Visual Studio Code\n\nBefore you can start using Visual Studio Code, you need to [download and install](https://code.visualstudio.com/download) the appropriate version for your operating system. Once the installation is complete, you can launch Visual Studio Code by double-clicking the icon on your desktop or by searching for \"Visual Studio Code\" in the start menu (on Windows) or spotlight (on macOS)."},{"Content":"## Opening a project\n\nWhen you first launch Visual Studio Code, you will be presented with an _Open Folder_ dialog. You can use this to open an existing project, or you can create a new one by clicking `File > New Folder` and then selecting the new folder. Once you have a folder open, you can start adding and editing files within that folder."},{"Content":"## The Visual Studio Code interface\n\nThe Visual Studio Code interface is divided into a few key areas:\n\n![VSCode Interface](https://code.visualstudio.com/assets/api/ux-guidelines/examples/architecture-containers.png){width: 50%}\n\n### Activity Bar\n\nThe Activity Bar is a core navigation surface in Visual Studio Code located on the left side of the window, provides access to various views and features such as the file explorer and debug console.\n\n### Primary Sidebar\n\nWill open the content of a Activity Bar Item.\n\n### Secondary Sidebar\n\nA secondary sidebar. You can drag items from other containers to here, to see more at a time.\n\n### Editor\n\nHere you will spend most of your time, writing and editing code.\n\n### Panel\n\nThe Panel is an other area to view containers.\n\n### Status Bar\n\nLocated at the bottom of the window, provides information and shortcuts for common actions."},{"Content":"## Writing and editing code\n\nVisual Studio Code has many powerful features for writing and editing code, including:\n\n- **Syntax highlighting**: Visual Studio Code automatically recognizes the type of code you're working with and applies color coding to make it easier to read and write.\n- **IntelliSense**: Visual Studio Code provides intelligent code completion and suggestions for many languages, making it easier to write code quickly and correctly.\n- **Code snippets**: Visual Studio Code includes a large collection of code snippets that can be inserted into your code with a few keystrokes, saving you time and effort.\n"},{"Content":"## Workspaces\n\nSelect `File > Open File / Open Folder` to open a file or a folder as a workspace.\n\n### Add to current Workspace\n\nSelect `File > Add Folder to Workspace...` then select a folder to add to your current Workspace.\n\n### Save Workspace\n\nSo you dont have to open all your folders in your workspace everytime you open Visual Studio Code, you can save your Workspace as a file. Select `File > Save Workspace As...` and choose a destination and name for your workspace file. Afterwards a _.code-workspace_ file was created. You can then open the workspace by just open the file or via `File > Open Workspace from File...`"},{"Content":"## Settings\n\nOpen Settings via `File > Preferences > Settings`.\n\n### User Settings\n\nWhen editing settings in the _User_ tap all settings apply globally across all Visual Studio Code Workspaces.\n\n### Workspace Settings\n\nWhen editing settings in the _Workspace_ tap all settings applied only effects the current opened workspace."},{"Content":"## Color Theme\n\nYou can customize the appearance of the Visual Studio Code interface to your needs. Navigate to `File > Preferences > Theme > Color Theme` there you can choose from a preinstalled Color Theme or install a new Color Theme with `+ Browse Additional Color Themes...`"},{"Content":"## Debugging and testing\n\nVisual Studio Code includes built-in support for debugging many languages, including JavaScript, TypeScript, and Python. To start debugging, you need to configure a \"launch.json\" file, which tells Visual Studio Code how to start and attach to your application. Once you have a launch configuration set up, you can start debugging by clicking the play button in the debug view or by pressing F5."},{"Content":"## Source Control\n\nVisual Studio Code support source control systems like Git, SVN, and TFS. The source control icon will appear on the left sidebar once you open a folder that is a git repository, allowing you to see the changes, make commits and handle merge conflicts, among other functionalities."},{"Content":"These are just a few of the many features that Visual Studio Code has to offer. With its powerful editing capabilities, built-in support for debugging and testing, and integration with source control, Visual Studio Code is a great choice for a wide range of development tasks. As you become more familiar with the tool, you'll find that it can help you work more efficiently and effectively."},{"Content":"# Using Visual Studio Code with InterSystems ObjectScript\n\nInterSystems ObjectScript is a programming language that is used in InterSystems databases such as InterSystems IRIS and Caché. Visual Studio Code (VSCode) is a popular text editor that can be used to write and edit ObjectScript code."},{"Content":"## Install InterSystems Extensions\n\nGo to the Activity Bar on the far left-hand side and click on the `Extensions` tap.\n\nType in the search bar *InterSystems* and then install the [InterSystems ObjectScript Extension Pack](https://marketplace.visualstudio.com/items?itemName=intersystems-community.objectscript-pack) . This Extension pack includes:\n\n* [InterSystems ObjectScript](https://marketplace.visualstudio.com/items?itemName=intersystems-community.vscode-objectscript): Adds InterSystems ObjectScript language support.\n* [InterSystems Language Server](https://marketplace.visualstudio.com/items?itemName=intersystems.language-server): Adds InterSystems ObjectScript language server.\n* [InterSystems Server Manager](https://marketplace.visualstudio.com/items?itemName=intersystems-community.servermanager): Define connections to InterSystems servers. Browse and manage those servers."},{"Content":"## Set ObjectScript Color Theme\n\nTo provide correct color highlighting when working with Objectscript choose a InterSystems Color Theme.\n\n* Navigate to `File > Preferences > Color Theme`\n* Choose `InterSystems Default Dark` or `InterSystems Default Light`."},{"Content":"## Connect to a Server\n\n* Open the `InterSystems Tools` tap in the Activity Bar.\n\n?[InterSystemsToolsIcon.png](InterSystemsToolsIcon.png){width:5%}\n\n* You can see default configuartions under `All Servers`\n* If you want to create a new Server connection click on the `+` button in the top and then fill in the connection properties:\n\n * *Name of new server definition*: Give your Connection a name.\n * *Optional Description*: A optional description for your server. Tap `Enter` to leave empty.\n * *Hostname or IP address of web server*: The Hostname or IP Address of your server.\n * *Port of web server*: The Port of the web server.\n * *Username*: The Username of your Iris user you want to connect from.\n * *Confirm connection type*: *http* or *https* connection type.\n\nAfter successfully adding a new Server you can see the Server under `All Servers`.\n\n> Tip: You can edit your Server Properties under: `...` in the top right corner `> Edit Settings > InterSystems: Servers > Edit in settings.json`.\n"},{"Content":"## Store Credentials\n\nEverytime you close Visual Studio Code and reopen it, it will prompt you a message to log in with your credentials. To avoid this you can store your credentials:\n\n- Store Username:\n - In the [Command Palette](KeyboardShortcuts.md#command-palette) type _Preferences: Open User Settings (JSON)_. Here you can see your server connection in a JSON format.\n - Choose the server you want to add your username and add a property called _username_\n\nExample:\n\n~~~json{copy}\n\"default~iris\": {\n \"webServer\": {\n ...\n },\n \"username\":\"\",\n \"description\": \"Connection to local InterSystems IRIS™ installed with default settings.\"\n }\n~~~\n\n- Store password\n - Next time you open Visual Studio code, it will prompt you a dialog and ask for a password\n - Type in your password\n - Click on the _key_ button in the top right corner of the dialog to store the password\n- Delete password\n - Navigate to _Accounts_ in the Activity Bar\n - Select your user\n - Click on `Sign Out`\n - select `Delete` when asked to delete password"},{"Content":"## Open a Namespace\n\n### Open a Namespace via InterSystems Tools\n\n- Open the `InterSystems Tools` tap in the Activity Bar.\n\n?[InterSystemsToolsIcon.png](InterSystemsToolsIcon.png){width:5%}\n\n- Choose a Server and click on it.\n - If the connection is succesfully you will see a new directory named _Namespaces_.\n - If the connection failed you will see _Unavailable at \\_.\n- Choose a namespace.\n - Click on the _eye-icon_ next to your namespace to view the namespace in your workspace.\n - Click on the _pen-icon_ next to your namespace to edit the namespace in your workspace.\n\n### Open a Namespace via the Explorer\n\n- Open the `Explorer` tap in the Activity Bar.\n- Do a right-click and choose `Add Server Namespace to Workspace`.\n- Now choose your Server or create a new one with the `+` in the top right corner of the dialog.\n- Choose a Namespace.\n"},{"Content":"## Writing ObjectScript Code\n\nOnce you have connected to a Namespace, you can begin writing ObjectScript code. Here are the basic steps for doing so:\n\n- Create a new file with the `.cls` file extension (for example, `MyPackage.MyClass.cls`)\n- Write your ObjectScript code in this file.\n- The class will be compiled after you saved the file.\n\nYou can also create other files with the extension .mac, .inc etc."},{"Content":"## Use SQLTools\n\nWhen you want to have a look inside your database without leaving Visual Studio Code, you can use the SQLTools extension.\n\n- Install the [SQLTools](https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools) extension\n- Install the [SQLTools InterSystems IRIS](https://marketplace.visualstudio.com/items?itemName=intersystems-community.sqltools-intersystems-driver)\n- Navigate to the SQLTools tap in the Activity Bar and choose `Add New Connection`\n- Choose _InterSystems IRIS_ and fill out the connection properties\n- Choose `Connect Now` and you will see a new connection under _Connections_\n- Now you can create a new SQl file and run the Query on your connection with `Run on active connection`\n **OR**\n- Browse through your tables and views, by unfolding the connection, and then view the table or view by clicking on the magnifyingglasses icon."},{"Content":"# Tips for using Visual Studio Code\n\nHere are a few tips to help you get the most out of Visual Studio Code.\n\n---\n\n## Editor Playground\n\nIf you just getting started with Visual Studio Code you can try the _Editor Playground_ under `Help > Editor Playground`. Here you learn how to use some of the powerful [code editing features](https://code.visualstudio.com/docs/editor/codebasics).\n\n## Use the Command Palette\n\nThe Command Palette is a powerful tool that allows you to access almost any feature or command in Visual Studio Code quickly and easily. Here you also can look up quickly the keybindings in case you forgt one. To open the Command Palette, press `Ctrl + Shift + P` (Windows/Linux) or `Cmd + Shift + P` (macOS).\n\n## Use the Built-in Terminal\n\nVisual Studio Code includes a built-in terminal that you can use to run command-line tools, such as git and npm. To open the terminal, press `Ctrl + Shift + Ö` (Windows/Linux) or `Cmd + Shift + Ö` (macOS).\n\n## Use Code Snippets\n\nVisual Studio Code includes a wide variety of built-in code snippets that can save you time when writing code. For example, you can type \"for\" and then press `Ctrl + Space` (Windows/Linux) or `Cmd + Space` (macOS) to quickly create a for loop. You can also create your own custom code snippets by going to `File > Preferences > Configure User Snippets`.\n\n## Customize the Theme\n\nVisual Studio Code includes several built-in themes that you can use to customize the look and feel of the editor. You can change the theme by going to `File > Preferences > Color Theme`. To find more themes you can use the marketplace.\n\n## Use Extensions\n\nVisual Studio Code has a vast ecosystem of [extensions](https://marketplace.visualstudio.com/) that add new features and functionality to the editor. You can browse and install extensions by going to `File > Extensions`. View [Essential visual Studio Code Extensions](EssentialExtensions.md).\n\n## Learn the Keyboard Shortcuts\n\nVisual Studio Code has a large number of keyboard shortcuts that can help you work faster and more efficiently. You can view the full list of keyboard shortcuts by going to `File > Preferences > Keyboard Shortcuts`, where you also can customize the keyboard shortcuts. View [Visual Studio Code Keyboard Shortcuts](KeyboardShortcuts.md).\n\n## Format on Save\n\nIts not always easy to keep your code in a good readable condition manually. Thankfully Visual Studio Code provides a formatter for almost every language. To format your file at the same time when you save your file, you can enable the `Format On Save` feature:\n\n- Open the Settings under `File > Preferences > Settings` or via `Ctrl + ,`\n- Type _Format On Save_\n- Check the checkbox for `Editor: Format On Save`\n\n## Sticky Scroll\n\nWhen working in code with long classes and methods that stretch beyond the vertical size of your screen, it can be difficult to keep track of which scope you’re working in. You may be editing a long method or exploring an unfamiliar codebase. Visual Studio Code now offers a feature to have a better overview of your code: **Sticky Scroll**\n\n- Open the Settings under `File > Preferences > Settings` or via `Ctrl + ,`\n- Type _Sticky Scroll_\n- Check the checkbox for `Editor › Sticky Scroll: Enabled`\n\n## Customize the settings\n\nVisual Studio Code allows you to customize various settings, such as the font size, tab size, and the number of spaces used for indentation. You can access these settings by going to `File > Preferences > Settings` (or `Code > Preferences > Settings` on a Mac). Here, you can edit the settings.json file, which contains all of your Visual Studio Code settings.\n\nHere you can find useful settings as well as\n\n- `editor.renderWhitespace`: render whitespaces in your code for better visibility.\n- `editor.tabSize`: customize the size of the tab\n- `editor.fontSize` : customise the size of the font\n\n## Settings Sync\n\nYou can sync your settings, keybindings and extensions across all devices with _[Settings Sync](https://code.visualstudio.com/docs/editor/settings-sync)_. Just log in with your GitHub or Microsoft user.\n\n## Use code navigation features\n\nVisual Studio Code includes features such as Go to Definition, Find All References, and Peek Definition, which can be useful for navigating through your codebase.\n\n## FiraCode Font\n\nInstall the [FiraCode Font](https://github.com/tonsky/FiraCode) to replace symbols that are encoded with several characters, such as `>=` or `!=`, to one symbol.\n\n## Create your own Extensions\n\nVisual Studio Code offers a wide variety of extensions and there is almost a extension for everthing. But sometimes you run into a problem where no extension exists to solve this problem. That could be the point to start [creating your own extensions](https://code.visualstudio.com/api/get-started/your-first-extension)."},{"Content":"# Visual Studio Code Keyboard Shortcuts\n\nA key to be more productive with Visual Studio Code is to use the builtin keyboard shortcuts. You can view the full list of keyboard shortcuts by going to `File > Preferences > Keyboard Shortcuts`.\n\n> Note: Some of these shortcuts may differ based on the specific operating system or keyboard layout you're using (here for german layout)."},{"Content":"## Keyboard reference sheet\n\nHere you can view almost all keyboard shortcuts in one overview.\n\n![Keyboard reference sheet](https://code.visualstudio.com/assets/docs/getstarted/tips-and-tricks/KeyboardReferenceSheet.png){width: 70%}\n\nFor [macOS](https://code.visualstudio.com/shortcuts/keyboard-shortcuts-macos.pdf) and [Linux](https://code.visualstudio.com/shortcuts/keyboard-shortcuts-linux.pdf)."},{"Content":"## Command Palette\n\nHere you can search and run all Commands. You can also map a command to a Keyboard shortcut here.\n\n- **Show Command Palette:**\n Windows/Linux: `Ctrl + Shift + P` MacOS: `Cmd + Shift + P`"},{"Content":"## View\n\n- **New window/instance:**\n Windows/Linux: `Ctrl + Shift + N` MacOS: `Cmd + Shift + N`\n\n- **Close editor:**\n Windows/Linux: `Ctrl + W` MacOS: `Cmd + W`\n\n- **Close window/instance:**\n Windows/Linux: `Ctrl + Shift + W` MacOS: `Cmd + Shift + W`\n\n- **Toggle Sidebar:**\n Windows/Linux: `Ctrl + B` MacOS: `Cmd + B`\n\n- **Close all open taps:**\n Windows/Linux: `Ctrl + K + W` MacOS: `Cmd + K + W`\n\n- **Fold {}:**\n Windows/Linux: `Ctrl + Shift + ß` MacOS: `Cmd + Shift + ß`\n\n- **Unfold {}:**\n Windows/Linux: `Ctrl + Shift + ´` MacOS: `Cmd + Shift + ´`\n\n- **Split editor view:**\n Windows/Linux: `Ctrl + ^` MacOS: `Cmd + ^`\n\n- **Zen Mode:**\n Windows/Linux: `Ctrl + K + Z` MacOS: `Cmd + K + Z`\n\n- **MarkDown Preview**:\n Windows/Linux: `Ctrl + K + V` MacOS: `Cmd + K + V`\n"},{"Content":"## Navigation\n\n- **Quick Open file from Workspace:**\n Windows/Linux: `Ctrl + P` MacOS: `Cmd + P`\n\n- **Next editor:**\n Windows/Linux: `Ctrl + Tab` MacOS: `Cmd + Tab`\n\n- **Previous editor:**\n Windows/Linux: `Ctrl + Shift + Tab` MacOS: `Cmd + Shift + Tab`\n\n- **Go back:**\n Windows/Linux: `Alt + Left Arrow` MacOS: `Ctrl + Cmd + Left Arrow`\n\n- **Go forward:**\n Windows/Linux: `Alt + Right Arrow` MacOS: `Ctrl + Cmd + Right Arrow`\n\n- **Global Find:**\n Windows/Linux: `Ctrl + Shift + F` MacOS: `Cmd + Shift + F`\n\n- **Show outline of file:**\n Windows/Linux: `Ctrl + Shift + .` MacOS: `Cmd + Shift + .`\n\n- **Go to specific Line:**\n Windows/Linux: `Ctrl + G` MacOS: `Cmd + G`"},{"Content":"## Edit\n\n- **Cut Line:**\n Windows/Linux: `Ctrl + X` MacOS: `Cmd + X`\n\n- **Copy Line:**\n Windows/Linux: `Ctrl + C` MacOS: `Cmd + C`\n\n- **Highlight:**\n Windows/Linux: `Shift + Left/Right Arrow` MacOS: `Shift + Left/Right Arrow`\n\n- **Highlight word:**\n Windows/Linux: `Ctrl + Shift + Left/Right Arrow` MacOS: `Cmd + Shift + Left/Right Arrow`\n\n- **Highlight word and put multiple cursor to next same words:**\n Windows/Linux: `Ctrl + D` MacOS: `Cmd + D`\n\n- **Select all occurrences of current selection:**\n Windows/Linux: `Ctrl + Shift + L` MacOS: `Cmd + Shift + L`\n\n- **Insert Snippet:**\n Windows/Linux: `Shift + Alt + J` MacOS: `Shift + Option + J`\n\n- **Copy Line:**\n Windows/Linux: `Shift + Alt + Up/Down Arrow` MacOS: `Shift + Option + Up/Down Arrow`\n\n- **Add cursor above/below:**\n Windows/Linux: `Ctrl + Alt + Up/Down` MacOS: `Ctrl + Option + Up/Down`\n\n- **Undo last cursor operation:**\n Windows/Linux: `Ctrl + U` MacOS: `Cmd + U`\n\n- **Comment line:**\n Windows/Linux: `Ctrl + #` MacOS: `Cmd + #`\n\n- **Comment highlighted array out:**\n Windows/Linux: `Shift + Alt + A` MacOS: `Shift + Option + A`\n\n- **Multiple Line cursor:**\n Windows/Linux: `Ctrl + Alt + Up/Down Arrow` or hold Alt and CLICK MacOS: `Cmd + Option + Up/Down Arrow` or hold Option and CLICK\n\n- **Select current Line:**\n Windows/Linux: `Ctrl + L` MacOS: `Cmd + L`\n\n- **Move line up/down:**\n Windows/Linux: `Alt + Up/Down Arrow` MacOS: `Option + Up/Down Arrow`\n\n- **Delete line:**\n Windows/Linux: `Ctrl + Shift + K` MacOS: `Cmd + Shift + K`\n\n- **Insert line below:**\n Windows/Linux: `Ctrl + Enter` MacOS: `Cmd + Enter`\n\n- **Insert line above:**\n Windows/Linux: `Ctrl + Shift + Enter` MacOS: `Cmd + Shift + Enter`\n\n- **Format Text:**\n Windows/Linux: `Shift + Alt + F` MacOS: `Shift + Option + F`"},{"Content":"# Essential Visual Studio Code Extensions\n\nVisual Studio Code is a powerful text editor, but it can be even more powerful with the right extensions. Here's a list of some essential extensions that can help you boost your productivity and streamline your workflow.\n\n---\n\n- **[Auto Complete Tag](https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-complete-tag)** - Extension Packs to add close tag and rename paired tag automatically for HTML/XML.\n\n- **[Auto Import](https://marketplace.visualstudio.com/items?itemName=steoates.autoimport)** - An extension that automatically imports missing modules as you type, saving you time and reducing the chance of errors.\n\n- **[Better Comments](https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments)** - Improve your code commenting by annotating with alert, informational, TODOs, and more!\n\n- **[Bookmarks](https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks)** - Mark lines and jump to them.\n\n- **[ChatGPT: write and improve code using AI](https://marketplace.visualstudio.com/items?itemName=timkmecl.chatgpt)** - Use ChatGPT right inside the IDE to enhance and automate your coding with AI-powered assistance (unofficial).\n\n- **[Code Runner](https://marketplace.visualstudio.com/items?itemName=formulahendry.code-runner)** - An extension that allows you to run your code directly from VSCode, with support for multiple languages and automatic output formatting.\n\n- **[Color Picker](https://marketplace.visualstudio.com/items?itemName=anseki.vscode-color)** - An extension that makes it easy to pick and use colors in your code, with a color picker and palette of common colors.\n\n- **[Console Ninja](https://marketplace.visualstudio.com/items?itemName=WallabyJs.console-ninja)** - JavaScript console.log output and runtime errors right next to your code.\n\n- **[Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome)** - An extension that allows you to debug your JavaScript code directly in Chrome, with full support for breakpoints, call stacks, and more.\n\n- **[Docker](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker)** - Makes it easy to create, manage, and debug containerized applications.\n\n- **[ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)** - An extension that helps you write more consistent and error-free code by linting your code for potential issues.\n\n- **[GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)** - An extension that adds powerful Git functionality to VSCode, including visualizations of changes, blame annotations, and more.\n\n- **[IntelliCode](https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode)** - The Visual Studio IntelliCode extension provides AI-assisted development features for Python, TypeScript/JavaScript and Java developers in Visual Studio Code, with insights based on understanding your code context combined with machine learning.\n\n- **[IntelliCode API Usage Examples](https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.intellicode-api-usage-examples)** - See relevant code examples from GitHub for over 100K different APIs right in your editor.\n\n- **[Jupyter](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter)** - Jupyter notebook support, interactive programming and computing that supports Intellisense, debugging and more.\n\n- **[Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)** - An extension that allows you to run a local web server and preview your HTML, CSS, and JavaScript changes in real-time.\n\n- **[Markdown Preview Enhanced](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced)** - Markdown Preview Enhanced ported to vscode.\n\n- **[Material Icon Theme](https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme)** - An extension that changes the default icons in the explorer to a more modern and minimalistic design.\n\n- **[Multiple cursor case preserve](https://marketplace.visualstudio.com/items?itemName=Cardinal90.multi-cursor-case-preserve)** - Preserves case when editing with multiple cursors.\n\n- **[open in browser](https://marketplace.visualstudio.com/items?itemName=techer.open-in-browser)** - This allows you to open the current file in your default browser or application.\n\n- **[Postman](https://marketplace.visualstudio.com/items?itemName=Postman.postman-for-vscode)** - Streamline API development and testing with the power of Postman, directly in your favorite IDE.\n\n- **[Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)** - An extension that automatically formats your code to conform to a consistent style.\n\n- **[SonarLint](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarlint-vscode)** - SonarLint is an IDE extension that helps you detect and fix quality issues as you write code in C, C++, Java, JavaScript, PHP, Python, HTML and TypeScript.\n\n- **[SQLTools](https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools)** - Connecting users to many of the most commonly used databases. Welcome to database management done right.\n\n- **[Thunder Client](https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client)** - Lightweight Rest API Client for VS Code.\n\n- **[Turbo Console Log](https://marketplace.visualstudio.com/items?itemName=ChakrounAnas.turbo-console-log)** - Automating the process of writing meaningful log messages.\n\n- **[vscode-spotify](https://marketplace.visualstudio.com/items?itemName=shyykoserhiy.vscode-spotify)** - Use Spotify inside vscode.\n\n- **[XML Tools](https://marketplace.visualstudio.com/items?itemName=DotJoshJohnson.xml)** - XML Formatting, XQuery, and XPath Tools for Visual Studio Code.\n\nThese are just a few examples of the many useful extensions available for Visual Studio Code. Experiment with different extensions to find the ones that work best for you and your workflow.\n\n---\n\nCheck out my Visual Studio Code Extensions:\n\n- **[VSToolKit](https://marketplace.visualstudio.com/items?itemName=PhilippBo.vstoolkit)** - Providing tools for Visual Studio Code.\n- **[OwnGitExtension](https://github.com/phil1436/owngitextension)** - A Visual Studio Code Extension that offers tools for working with GitHub projects.\n- **[OwnObjectScriptExtension](https://marketplace.visualstudio.com/items?itemName=PhilippB.ownobjectscriptextension)** - A Visual Studio code extension that supplies tools for InterSystems ObjectScript.\n"}]},{"Title":"Java Introduction","Password":"123","Language":"english","Author":"pbonin","Description":"A java Introduction","IsPrivate":false,"Pages":[{"Content":"![logo](https://upload.wikimedia.org/wikipedia/en/thumb/3/30/Java_programming_language_logo.svg/1200px-Java_programming_language_logo.svg.png){height:300px}\n\n# Install Java\n\n## Windows\n\n1. Download the latest version of Java from [here](https://www.oracle.com/java/technologies/javase-downloads.html).\n2. Run the installer and follow the instructions.\n\n### Linux\n\n1. Open a terminal and run the following command:\n\n```bash\nsudo apt install default-jre\n```\n\n2. Enter your password and press enter.\n3. Wait for the installation to finish.\n\n#### Mac\n\n1. Download the latest version of Java from [here](https://www.oracle.com/java/technologies/javase-downloads.html).\n2. Run the installer and follow the instructions.\n\n"},{"Content":"# Hello World\n\n1. Open a new file in your favorite text editor and save it as `Hello.java`.\n2. Copy the following code into the file:\n\n~~~java\npublic class Hello {\n public static void main(String[] args) {\n // TODO\n }\n}\n~~~\n\n3. Replace the `TODO` so that _\"Hello World\"_ will be printed to the console.","Hint":"Use the `System.out.print` method to print the string _\"Hello World\"_ to the console.","Result":"_Hello.java_:\n\n~~~java\npublic class Hello {\n public static void main(String[] args) {\n System.out.print(\"Hello World\");\n }\n}\n~~~\n"},{"Content":"# Using string variables\n\nYou can define a variable by assigning a value to it. The variable name is on the left of the assignment operator `=` and the value is on the right.\n\n~~~java\nString name = value;\n~~~\n\nDefine a variable called `name` and assign it the value `\"John\"` and then print it to the console.","Result":"~~~java\nString name = \"John\";\nSystem.out.print(name);\n~~~"}]},{"Title":"IRIS Git HowTo","Language":"english","Author":"aschuetz","Description":"A tutorial for how to use git with IRIS","IsPrivate":false,"Pages":[{"Content":"![GIT](https://git-scm.com/images/logos/downloads/Git-Icon-Black.png){width:10%}\n\n# Using Git Source Control and Continuous Delivery with InterSystems IRIS\n\nSource control is crucial for managing and tracking changes in software development projects. It provides a systematic approach to versioning, collaboration, and history tracking, allowing developers to work on different aspects of a project simultaneously while maintaining a coherent and stable codebase. Git, a distributed version control system, has gained widespread popularity due to its efficiency, flexibility, and robust branching and merging capabilities. Git enables teams to collaborate seamlessly, facilitates code review processes, and ensures a reliable and auditable history of changes. Its decentralized nature allows for offline work and fosters a more resilient and scalable development environment. In essence, using source control, especially Git, is essential for promoting collaboration, preserving code integrity, and streamlining the software development lifecycle."},{"Content":"## Git for client-sided development with VSCode\n\nVisual Studio Code enables developers to store their IRIS source code on the client device. This allows to easily use most source control tools like Git as in any other software project. Client-sided development with VS Code is the recommended way for new projects. \n\nHowever some tasks e.g. creating Productions may still take place in the IRIS Management Portal. These source code files created in the Browser need to be synced manually to the client device and can then be added to the local Git repository. \n\nTo export a file from the server use the Visual Studio Code Objectscript Explorer. Find the class in the classes tree and do an export via the right-click menu. This step has to be repeated after every change to the file from a different location. A production with the default name for example can be found in a package the \"*NAMESPACE*+PKG\".\n\n?[export_server_file.png](export_server_file.png){width:50%}"},{"Content":"## Git for server-sided development using Git extension\n\nWhen using shared development environments for IRIS projects the single source of truth is the server. Therefore, the source control needs to take place on the IRIS server. IRIS allows to integrate third-party source control systems using [Source Control Hooks](https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GSTD_Hooks). This requires some development effort. Luckily for Git there exist multiple extensions which can be downloaded via [InterSystems Open Exchange](https://openexchange.intersystems.com/). The extension used in this document is called [Git for Shared Development Environments](https://openexchange.intersystems.com/package/Git-for-Shared-Development-Environments). This extension can be installed via InterSystems Package Manager (IPM)."},{"Content":"### Install InterSystems Package Manager (IPM)\n\nThe installation of the InterSystems Package Manager applies only to a specific workspace. The package manager used to be called ZPM. But since it is now officially maintained by InterSystems it was renamed.\n\n1. Download the latest version of the package manager from the InterSystems registry: [Link](https://pm.community.intersystems.com/packages/zpm/latest/installer).\n2. Open the IRIS Management Portal\n3. Go to **System Explorer** -> **Classes**, select the namespace and click on import.\n4. Select the previously downloaded package manager file (.xml)\n5. Make sure compile imported items is enabled. Then click on **Next** and **Import**\n\n?[import_ipm.png](import_ipm.png){width:50%}"},{"Content":"### Install Git\n\nBefore installing the Git extension Git itself must be installed on the IRIS server. Go to the [Git homepage](https://git-scm.com/downloads) and download the version suiting your operating system. Follow the instructions to install Git into your system."},{"Content":"### Install Git for Shared Development Environments\n\nIn this example the name of the IRIS Installation is `IRIS` and the namespace is `GITDEMO`. Replace according to your configuration. The installation of the Git extension applies only to a specific namespace. InterSystems Package Manager (IPM) and Git must be installed beforehand.\n\n1. On Unix systems open an IRIS Terminal with `iris TERMINAL IRIS`. On Windows based systems right click on the IRIS cube tray icon and click on `Terminal`.\n\n?[windows_open_terminal.png](windows_open_terminal.png){width:30%}\n\n2. Run the following commands: \n\n```Objectscript\nzn \"GITDEMO\"\nzpm \"install git-source-control\"\n```\n\n?[install_git_extension.png](install_git_extension.png){width:50%}"},{"Content":"### Prepare SSH Key Pair\n\nTo be able to synchronise the IRIS server-sided Git repository with a remote repository a SSH key pair needs to be generated or imported. The private key needs to be present on the IRIS host and the corresponding public key must be accepted by the remote repository. If you only want to use git locally on the IRIS host this step can be skipped.\n\nA detailed guide how to generate a key pair can be found in the [Gitlab Documentary](https://docs.gitlab.com/ee/user/ssh.html#generate-an-ssh-key-pair). The tool ssh-keygen is usually pre-installed on Unix systems and can be found in the IRIS instance directory `\\bin` on windows systems. Make sure to keep the passphrase empty.\n\nIf the remote repository is a Gitlab instance the guide set the guide as a project- or server-wide deployment key can be found [here](https://docs.gitlab.com/ee/user/project/deploy_keys/#create-a-project-deploy-key). If a different Git remote server is used check the documentary how to set a deployment key.\n\nNote the path to the private key on the IRIS host for the configuration of the Git extension in the next chapter."},{"Content":"### Configure Git for Shared Development Environments\n\nGit needs some namespace wide configuration. So, for every namespace the following steps must be performed once:\n\n1. To configure the Git extension some information is required. Note these information before running the configuration method:\n- **Git binary Path**: On Unix-based systems you can use the command `whereis git`. On windows it is usually `C:\\Program Files\\Git\\bin\\git.exe` or the path specified during the setup.\n- **Temp folder**: Define a path where the Git repository resides on the IRIS server. It is a good idea to include the name of the namespace in the path as Git might be used in different namespaces on the same IRIS host (e.g. `C:\\Code\\IRIS\\GITDEMO` or `\\home\\irisuser\\code\\IRIS\\GITDEMO`). Make sure at least the topmost folder of the path doesn't exist yet (in this example `GITDEMO`). It will be created by the Git extension. This avoids ownership and access rights issues especially on Windows systems.\n- **Path to private key**: See last chapter. By default the path is `/.ssh/id_rsa` or `.../id_ed25519`. But it might be different if a different path was specified generating or importing the key.\n- **% Replacement character**: IRIS uses the character `%` in some class names which is not allowed as file name. So before exporting the classes to the Git repository this character must be replaced. Make sure to use a replacement character that is not used in class names. The character `_` might be an appropiate choice.\n- **Default Pull EventHandler**: A custome pull event handler can be specified. This is in most cases not required as there is default pull event handler which should be suitable for most projects.\n\n2. In a terminal or CMD session open an IRIS Terminal using the command `iris TERMINAL IRIS` and run the following commands. Fill in the previously noted values when prompted.\n\n```Objectscript\nzn \"GITDEMO\"\ndo ##class(SourceControl.Git.API).Configure()\n```\n\n\n?[git_config.png](git_config.png){width:50%}"},{"Content":"### Running served-sided Git commands\n\nThere are multiple ways to run Git commands on the server:\n\n1. **Visual Studio Code**: Select any class file. The `source control` menu can be found in the top right corner.\n\n?[vscode_button.png](vscode_button.png){width:50%}\n\n2. **IRIS Studio**: The `source control` menu can be found in the top of the window in the menu bar.\n\n3. **Production Configuration**: In the production configuration server-sided Git commands can be triggered via a the source control button in top right. Next to the source control button \n\n?[production_config_button.png](production_config_button.png){width:50%}\n?[source_control_output.png](source_control_output.png){width:50%}"},{"Content":"### Initializing Git Repository and adding remote repository\n\nOn some operating systems the username and email must be set again. To check if it needs to be updated use any of the three options from the previous chapter to launch the `Settings` view from the source control menu. Check if the `username` and `email` values are set. If not fill them out again and click on `save`.\n\n?[git_settings.png](git_settings.png){width:50%}\n\nAlso from the source control menu trigger the `init` or `initialize` command. Next create a new branch (e.g. named `main`) via the menu. Notice that the menu items change after the repository is initialized.\n\nOn Windows systems go to repository directory on the IRIS server and start a Git-Bash using the right click context menu. On a Unix system launch a terminal and go to the repository directory. Use the following command to add a remote repository:\n\n```\ngit remote add origin git@:/gitdemo.git\n```\n\nOnly SSH remotes are supported, HTTP(s) remotes won't work.\n"},{"Content":"### Adding and commit files\n\nTo add a file to source control select the file or go to a production and select `Add` in the source control menu. To commit changes use `Commit changes to file`. \n\n?[add_file.png](add_file.png){width:20%}\n?[commit_changes.png](commit_changes.png){width:20%}\n\nTo see the latest commits and other information like the local branches, remote branches and the active branch use the Git UI which can also be started from the source control menu.\n\n?[git_ui.png](git_ui.png){width:50%}"},{"Content":"### Push, Fetch and Pull\n\nSimilar to adding and commiting files the changes can be pushed to a remote repository via the source control menu or fetched/pulled to the local repository.\n\n?[windows_git_menu.png](windows_git_menu.png){width:50%}"},{"Content":"## Continuous Delivery (CD)\n\nThe idea behind an continuous delivery is to provide a mechanism to push the latest changes to production but not fully automated but only after being approved by a human.\n\nThis chapter will use Gitlab, Gitlab CI and Gitlab Runner to build a continuous deoloyment pipeline. But the same principles can also be applied to other platforms.\n\nIn a scenario where CD applies the production system is sperated ideally on a seperate machine but a sperate IRIS instance or namespace will also work. After developing and testing on one or more seperate system the code is ready to be deployed to the production system. To start the CD process a merge/pull request is made to merge the code from the development branch (e.g. main oder dev) to a production branch. Once the merge/pull request is approved the changes will automatically be deployed to the production system. It is possible and often recommended to add addtional steps like automated tests or staging environment to a Gitlab CI pipeline.\n\nTo set up a simple CD pipeline the following components are required.\n\n- **Gitlab Server**: Platform that hosts the Git repository and allows the maintainer to make pull/merge requests.\n- **Gitlab Runner**: An application that runs on the production system and gets triggered by the Gitlab server. The Gitlab Runner will first pull the latest version of the code and then apply the changes to the production system.\n- **Import function**: A function in the IRIS environment that gets called by the Gitlab Runner. It contains the logic that is required to import and apply the changes.\n- **Gitlab CI yaml file**: A file inside the Git repository that tells the Gitlab Runner on the productin system what to do."},{"Content":"### Installing and registering the Gitlab Runner\n\nThere are differenct types of Gitlab Runners but for the CD use-case it is highly recommended to use a specific runner and make sure the runner can not be missused to inject code into the production system.\n\n1. Create a new branch by going to `Repository` -> `Branches` -> `New Branch` in Gitlab Web Client. \n2. Go to `Settings` -> `Repository` -> `Protected Branches`. The select the branch created in the previous step. Set `Allowed to merge` to *Maintainers* and `Allowed to push` to *No one*. Make sure all other protected branches do not allow `Developers` to push or merge code.\n3. All Members of the repository with the role `maintainer` can trigger the CD pipeline and therefore push code to the production system. At this point you might want to check who is a `maintainer` by going to `Settings` -> `Members`. Degrade all members to the `Developer` role who should not be able to trigger the CI pipeline.\n4. Install the Gitlab Runner on the production system. The steps to do this can be found in the [Gitlab documentary](https://docs.gitlab.com/runner/install/).\n5. Go back to your Gitlab Repository, go to `Settings` -> `CI/CD` and expand the `Runners` section. Find the `Set up a specific Runner manually` section and note the URL and the registration token.\n\n?[specific_runner_registration.png](specific_runner_registration.png){width:30%}\n\n6. Switch back to the production system and register the runner by running the following command. Enter the URL and token from the step before. Enter a description and tags (e.g. `IRIS,production`) to identify the runner. Enter `shell` as an executor.\n\n```bash\ngitlab-runner register\n```\n\n?[runner_registration_success.png](runner_registration_success.png){width:30%}\n\n7. In the Gitlab go back to `Settings` -> `CI/CD` and expand the `Runners` section. The runner you just registered should now appear. Click on the settings button to open the runners settings.\n\n?[runner_view.png](runner_view.png){width:30%}\n\n8. Set the settings of the runner as following:\n \n- **Active**: *enabled*\n- **Protected**: *enabled*\n- **Run untagged jobs**: *disabled*\n- **Lock to current projects**: *enabled*\n\n?[runner_settings.png](runner_settings.png){width:30%}"},{"Content":"### Create import function\n\nConnect to your IRIS instance and namespace using the editor of your choice. Create a new class. The following code snippet shows a simple example how this class might look. It will only load new `cls` files and ignore everything else. Also it will not handle classes that are deleted from the repository. Adopt the class to your requirements. `$system.Util.GetEnviron(%String)` can be used to access [Gitlab-Runner variables](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html). Those variables contain usefull information like how the pipeline was triggered. These could be used to add additonal security checks like if the trigger was a `merge` and the target branch actually is `prod`.\n\n~~~Objectscript{copy}\nClass CI.Git\n{\n\nClassMethod getDir() [ CodeMode = expression ]\n{\n##class(%File).NormalizeDirectory($system.Util.GetEnviron(\"CI_PROJECT_DIR\"))\n}\n\n/// Load code from Gitlab CI into IRIS\nClassMethod Load() As %Status\n{\n try {\n\t\tset dir = ..getDir()\n\t\t\n // cuk = compile after loading, update only, keep source\n\t\tdo $system.OBJ.ImportDir(dir, \"*.cls\", \"cuk\", .errors, 1)\n\t\tthrow:$get(errors,0)'=0 ##class(%Exception.General).%New(\"Load error\")\n\t\t\n\t\thalt\n\t} catch ex {\n\t\twrite !,$System.Status.GetErrorText(ex.AsStatus()),!\n\t\tdo $system.Process.Terminate(, 1)\n\t}\n}\n\n}\n~~~"},{"Content":"### Create a gitlab-ci.yml file\n\nCreate a file named `.gitlab-ci.yml` in the root directory of your git repository. You can do this in the Gitlab WebUI or locally using an editor of your choice. A simple gitlab-ci file may look like this:\n\n~~~yaml{copy}\nstages:\n - deploy\n\nload prod server:\n only:\n - prod\n tags:\n - iris\n stage: deploy\n script: \n - iris session IRIS -U \"CI_TEST\" \"##class(CI.Git).Load()\"\n~~~\n\nThis CI/CD configuration will have the stage `deploy`. It will run the job `load prod server` but only when the target branch is `prod`. It identifies the desired runner by the tag `iris` and will call the `CI.Git.Load()` method in the namespace `CI_TEST`.\n\nInstead of writing a custom import/load function you could also have a look at the [InterSystems Open Exchange](https://openexchange.intersystems.com) and use e.g. the [ISC DEV](https://openexchange.intersystems.com/package/ISC-DEV) extension."},{"Content":"## Troubleshooting\n\n### zpm install git.source-control fails (Windows)\n\nOn windows systems with ZPM/IPM prior the version 0.51 there is a bug that prevents the Git plugin from being installed ([Gitlab Issue](https://github.com/intersystems/ipm/issues/357)). The error looks like this:\n\n```Terminal\n[TRAINING|git-source-control] Reload SUCCESS\n[git-source-control] Module object refreshed.\n[TRAINING|git-source-control] Validate START\n[TRAINING|git-source-control] Validate SUCCESS\n[TRAINING|git-source-control] Compile START\n[TRAINING|git-source-control] Compile SUCCESS\n[TRAINING|git-source-control] Activate START\n[git-source-control] Activate FAILURE\nERROR! Running command returned error.\n```\n\nTo resolve the issue use a newer version of ZPM/IPM if available or fix the issue manually. To do so use IRIS Studio or the Objectscript plugin in Visual Studio Code. \n\n1. Select your IRIS server and the namespace where the issue should be fixed.\n\n?[zpm_fix_1.png](zpm_fix_1.png){width:30%}\n\n2. Go to `Classes` -> `%ZPM` -> `%PackageManagaer` -> `Developer` -> `File,cls`. In VS Code do a right-click on the file and click `Export`. Now you should be able to edit the file. In IRIS Studio the file can be edited by just opening it.\n\n?[zpm_fix_2.png](zpm_fix_2.png){width:50%}\n\n3. Search for `\"ROBOCOPY\"`. There should be two matches. In the following lines finde the line \n\n```Objectscript\nif $$$ISERR(tSC),$Get(retCode)=1||($Get(retCode)=2) set tSC=$$$OK\n```\n\nAnd replace it with this line:\n\n```Objectscript\nif $$$ISERR(tSC),$Get(retCode)<8 set tSC=$$$OK\n```\n\n?[zpm_fix_3.png](zpm_fix_3.png){width:50%}\n\nSave and compile the file. If a propmt appears that asks to overwrite the file on the server click on `overwrite`.\n\nContinue with the installation of `git.source-control`."},{"Content":"### Dubious ownership error (Windows)\n\nIf the commit button does not appear after adding a file to source control and/or the following error appears in the Git WebUI there a problem with the ownership of the Git repository folder.\n\n?[web_ui_error.png](web_ui_error.png){width:50%}\n\nTo resolve the issue follow these steps:\n\n1. Go to the path of the folder in Windows Explorer and right-click on the folder. Open `Properties`.\n\n?[ownership_fix_1.png](ownership_fix_1.png){width:20%}\n\n2. Go to the tab security and click on `Adanced`.\n\n?[ownership_fix_2.png](ownership_fix_2.png){width:20%}\n\n3. Click on `Change` next to the owner of the folder.\n\n?[ownership_fix_3.png](ownership_fix_3.png){width:30%}\n\n4. Type `\\Administrators` and click ok. Make sure to use the group Administrator**s** instead of the user Administrator. If Windows was installed using a different language the group name migth be different (For example `Administratoren` in German).\n\nAfter the steps above the file can't be commited before removing the file from source control and re-adding it. The `Commit` command should now be visible in the source control menu. If the problem still exists delete the folder and re-create it by calling the `Init` command from the source control menu. Make sure to backup the folder before doing so.\n"}]},{"Title":"BASICS InterSystems IRIS","Password":"1978ISC","Language":"english","Author":"pbonin","Description":"Development with ObjectScript and advanced concepts","IsPrivate":false,"Pages":[{"Content":"## Introduction\n\nIn the following exercises, you will create a simple data model step by step to implement an\nenterprise resource planning system.\n\nBased on this, data is generated for the model and then various techniques are demonstrated and\npracticed working with the data. Object access as well as the various possibilities with SQL will be\nused to process the data.\n\nAnother role will be the implementation of logic using class and instance methods.\n\nFinally, the different projections (XML, JSON) will be used to implement web services (SOAP and\nREST).\n\nThe practical exercises will be complemented by demos of various other features.\n"},{"Content":"?[FoundationsTraining_IMG1.png](FoundationsTraining_IMG1.png){width:80%}\n\nIn the following exercises, the above model will now be successively built up and enriched with\nvarious methods.\n\n**It is strongly recommended to use the identifiers as indicated in the exercise script, as the exercises build on each other.**"},{"Content":"## Exercise 1 – Persistent vs. Registered vs. Serial\n\nIn InterSystems IRIS there are different possibilities to define data classes. A distinction is made between:\n\n- **Persistent** : Objects of this type can be stored permanently and have an SQL projection by\n default.\n- **Registered** : Objects of this type exist only in the memory and are therefore volatile.\n- **Serial** : These objects are embedded in persistent or registered objects and have no identity\n of their own in the database. If the embedding object is destroyed, the embedded object is\n also destroyed.\n\n1. Let's start by defining the class **`Training.Costumer`** for storing the customer in the database. This is a persistent class, because customer objects should be stored permanently and should also be accessible via SQL.\n2. The class should first have the two properties\n\n- a. **CustomerNr %Integer**\n- b. **Name %String**\n\n3. Define for the property **CustomerNr the MINVAL = 1**. Since we want to use the data generator of\nIRIS later, the class should first inherit from **_%Persistent_** and **_%Populate_**.\n\nIt should not be possible to save the customer without specifying a CustomerNr and a Name.\nTherefore we define these two properties as **_required_**.","Hint":"Use this construct:\n\n~~~objectscript\nClass Training.Customer Extends (SuperClass1, SuperClass2)\n{\nProperty PropName1 As Type [ Required ];\n\nProperty PropName2 As Type (MINVAL = 1) [ Required ];\n\n}\n~~~","Result":"~~~objectscript\nClass Training.Customer Extends (%Persistent, %Populate)\n{\n\nProperty CustomerNr As %Integer(MINVAL = 1) [ Required ];\n\nProperty Name As %String [ Required ];\n\n}\n~~~"},{"Content":"In the next step, a serial class is added to the Customer class. This class describes the address of the\ncustomer.\n\nDefine a class Training.Address. The class has the following properties\n\n- a. **City %String**\n- b. **Postcode %Integer**\n- c. **Street %String**\n\nand extends **_%SerialObject_** and **_%Populate_**.\n\nThe operation of the data generator can be controlled by the POPSPEC parameter.\n\n- Specify the POPSPEC parameter for the three properties. The data for the City should be\n generated with the **_City()_** method. For the Postcode please specify **_USZip()_** and for Street\n **_Street()._** The resulting class definition looks like this:\n","Hint":"_POPSPEC_ example:\n\n~~~objectscript\n...\nProperty Age As %Integer(POPSPEC = \"Age()\");\n...\n~~~","Result":"~~~objectscript\nClass Training.Address Extends (%SerialObject, %Populate)\n{\n\nProperty City As %String(POPSPEC = \"City()\");\n\nProperty Postcode As %Integer(POPSPEC = \"USZip()\");\n\nProperty Street As %String(POPSPEC = \"Street()\");\n\n}\n~~~"},{"Content":"The next step is to link the customer class and the address class. For this purpose, a property\n**_Address_** of the type **_Training.Address_** is defined in the customer class.\n\nSave and compile the classes. In the next exercise, data will be generated for the customers and the\nSQL projection will be examined.","Result":"~~~objectscript\nClass Training.Customer Extends (%Persistent, %Populate)\n{\n\nProperty CustomerNr As %Integer(MINVAL = 1) [ Required ];\n\nProperty Name As %String [ Required ];\n\nProperty Address As Training.Address;\n\n}\n~~~"},{"Content":"## Exercise 2 – Data generation and first queries\n\n1. To generate the data, open a terminal window from the IRIS Cube's menu in the taskbar.\n2. Switch to the namespace **_Training_** with: **_zn \"TRAINING\"_**\n3. In the namespace **_TRAINING_** enter: **_write ##class(Training.Customer).Populate(100)_**\n4. **_What output do you get?_**\n","Result":"The Populate method calls the data generator. This has been added to the Customer class by extending the %Populate class. The Populate method returns the number of objects/data sets created. In our example, the current parameter 100 indicates that 100 customer objectsshould be created."},{"Content":"5. Check that 100 customers have been created. To do this, start the **_System Management Portal_** and go to **_Explorer_** and then **_[SQL](http://$$HOST$$:$$PORT$$/csp/sys/exp/%25CSP.UI.Portal.SQL.Home.zen?$NAMESPACE=TRAINING&$NAMESPACE=TRAINING)._** Make sure that you are in the **_TRAINING_** namespace. Use SQL to count the number of customer datasets.\n6. Display the customer datasets. **_How is the address data displayed?_**","Result":"By their ID."},{"Content":"7. Start the [Class Reference](http://$$HOST$$:$$PORT$$/csp/documatic/%25CSP.Documatic.cls) from the IRIS Cube and change to the namespace **_TRAINING_**.\n8. Search the documentation of your classes\n\nClasses are automatically documented in the Class Reference. This documentation is\ngenerated dynamically when it is called and is therefore always up to date. For the\ndocumentation there is a special form of comments. These are introduced line by line by\nthree slashes.\n\n9. Add comments preceded by `///` to the properties in the customer class.\n10. Save and compile the class and check the result in the Class Reference.\n"},{"Content":"## Exercise 3 – Object access in ObjectScript\n\nIn this exercise you will learn about object access. All persistent objects have a unique ID or OID in the database. This ID is not reassigned by the system even after the object is deleted.\n\nTo interact with persistent objects in InterSystems IRIS, the following methods play an important role.\n\n- **_%New()_** Class method for creating a new in-memory instance of the class\n- **_%OpenId(...)_** Class method for opening objects in the database\n- **_%Save()_** Instance method for saving AND updating objects\n- **_%DeleteId(...)_** Class method for deleting objects from the database"},{"Content":"1. Go to the System Management Portal in the [SQL Explorer](http://$$HOST$$:$$PORT$$/csp/sys/exp/%25CSP.UI.Portal.SQL.Home.zen?$NAMESPACE=TRAINING&$NAMESPACE=TRAINING) and execute the statement:\n\n```\nselect * from Training.Customer\n```\n\n2. Look at the **_ID_** column. The IDs assigned by the system are displayed here.\n3. Start a terminal from the IRIS Cube and use one of the existing IDs and open the corresponding object with **`%OpenId()`** method.","Result":"```\nTRAINING>set k = ##class(Traing.Customer).%OpenId(1)\n```"},{"Content":"```\nTRAINING>set k = ##class(Traing.Customer).%OpenId(1)\n```\n\nIf there is an object of the class Training.Customer with the ID = 1 in the database, the variable `k` then contains an object reference to this object in the memory.\n\n4. **_What does the access to the properties of the object look like?_**","Hint":"This object can be accessed using this object reference k and the properties of the object can be accessed using the dot syntax.\n\nThe access can be both read and write.","Result":"```\nTRAINING>write k.Name\nZevon,Paul N.\n```"},{"Content":"5. Change the name of the customer and then save it by calling the **_%Save()_** method for this object\n6. Verify that your changes are also visible via SQL.\n\nThe **_%Save()_** method returns a status code indicating whether the %Save() was successful or not. A status code = 1 indicates successful save and a status code != 1 indicates a failed save attempt.","Result":"```\nTRAINING>set k.Name = \"Smith,John A.\"\nTRAINING>write k.%Save()\n1\n```"},{"Content":"7. Create a new customer in the terminal using the **_%New()_** method and save it to the database","Result":"```\nTRAINING>set newCustomer = ##class(Traing.Customer).%New()\nTRAINING>set newCustomer.Name = \"Taylor,James K.\"\nTRAINING>write newCustomer.%Save()\n1\n```"},{"Content":"## Exercise 4 – A first class method\n\nIn InterSystems IRIS and in ObjectScript there are two types of methods. **Class methods** and **instance methods**. Class methods are often called static methods in other environments and can be used without a concrete object instance, but for this reason of course it cannot access properties within an object. There is at that moment no object to which the method belongs.\n\nInstance methods operate on a concrete object and can accordingly also access properties or other instance methods of this object. The access takes place here with the **`..`** syntax. That is, two consecutive .. followed by the referenced property or method to be called.\n\nIn this exercise, you will create an initial class method to create and attach a new Customer object."},{"Content":"1. Consider the code below and try to explain it.\n\n~~~objectscript{copy}\nClassMethod CreateCustomer(\n\tpCustomerNr As %Integer,\n\tpName As %String,\n\tpStreet As %String,\n\tpPostcode As %Integer,\n\tpCity As %String,\n\tOutput pId As %Integer) As %Status\n{\n #dim tCustomer as Training.Customer = ..%New()\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n\n try {\n set tCustomer.CustomerNr = pCustomerNr\n set tCustomer.Name = pName\n set tCustomer.Address.Street = pStreet\n set tCustomer.Address.Postcode = pPostcode\n set tCustomer.Address.City = pCity\n $$$ThrowOnError(tCustomer.%Save())\n set pId = tCustomer.%Id()\n } \n catch tEx {\n write !,tEx.DisplayString()\n set tSc = tEx.AsStatus()\n }\n\n return tSc\n}\n~~~","Result":"The method returns the status as return value and the ID of the newly created Customer object via an **_output parameter._** The error handling is done in a **_TRY-CATCH block._** `$$$ThrowOnError()` is a **_system macro_** that throws an exception if the evaluation of the expression results in an error condition.\n\nPlease also note the `#dim ...` statements. This is a pseudo declaration that does not create overhead at runtime but supports intellisense."},{"Content":"2. Define the method in your **_Training.Customer_** class, save and compile the class and then test the method at the terminal."},{"Content":"## Exercise 5 – Calculated properties and object references\n\nIn this exercise, the model is further completed and the Order and OrderPositions classes are added.\n\nIn addition, Relationships and Object references are introduced and the differences are explained."},{"Content":"1. Create a class **_Training.Order_**. The class should extend **_%Persistent_** and **_%Populate_** and\n\n- a. **_OrderNr_** %Integer\n- b. **_OrderDate_** %Date (MINVAL = 63918)\n- c. **_Customer_** Training.Customer\n\n2. Note that the Customer property refers to the persistent class **_Training.Customer_**. This is an object reference.","Result":"~~~objectscript\nClass Training.Order Extends (%Persistent, %Populate)\n{\n\nProperty OrderNr As %Integer;\n\nProperty OrderDate As %Date(MINVAL = 63918);\n\nProperty Customer As Training.Customer;\n\n}\n~~~"},{"Content":"//3. Examine the **_Training.Article_** class in the Class Reference. This class (pre-installed) is needed for the **_Training.OrderPositions_** class that will now be defined.\n\n3. Download the **Training.Article** class below and navigate in the Management Portal to _[System Explorer > Classes](http://$$HOST$$:$$PORT$$/csp/sys/exp/%25CSP.UI.Portal.ClassList.zen?$NAMESPACE=TRAINING&$NAMESPACE=TRAINING)_ and import the just downloaded class\n\n$$$[Download Training.Article](Article.cls)\n\n4. Create a **_Training.OrderPositions_** class. This class extends **_%Persistent_** and **_%Populate_** and gets first the properties:\n\n- a. **_Article_** Training.Article\n- b. **_Quantity_** %Integer (MINVAL = 1, MAXVAL = 5)","Result":"~~~objectscript\nClass Training.OrderPositions Extends (%Persistent, %Populate)\n{\n\nProperty Article As Training.Article;\n\nProperty Quantity As %Integer (MINVAL = 1, MAXVAL = 5);\n\n}\n~~~"},{"Content":"5. In this step, you define a calculated and SQL-computed property for the row value of this item. Calculated properties have no in-memory representation of the value, but the value is calculated when the property is accessed. To define such a calculated property, the parameters **_Calculated, SqlComputed_** and the property **_SqlComputeCode_** must be defined.\n\nThe property definition thus looks like this:\n\n~~~objectscript{copy}\nProperty SumTotal As %Numeric [ Calculated, SqlComputeCode = { set {*} =\n {Quantity} * ##class(Article).%OpenId({Article}).MarketPrice}, SqlComputed];\n~~~\n\nIn the SqlComputeCode the amount is determined as the product of the Property Quantity * Article MarketPrice. \n\n**_Look at the code and try to explain it._**"},{"Content":"Now, before linking Order and OrderPositions, let's take a closer look at the Customer object reference in the **_Training.Order_** class.\n\n6. Go back to the terminal or restart the terminal from the IRIS Cube.\n7. Create 100 orders with the following command.\n\n```\nTRAINING>write ##class(Training.Order).Populate(100)\n100\n```\n\n8. Check via the System Management Portal that the orders have been created.\n9. Take an order ID and open the associated order object on the terminal\n\n```\nTRAINING>set a = ##class(Training.Order).%OpenId(1)\n```\n\n10. To check that the object has been opened, use the `$isobject()` function. This returns 1 if the passing OREF is valid, 0 otherwise.\n\n```\nTRAINING>write $isobject(a)\n1\n```\n\n11. Enter the following\n\n```\nTRAINING>write a.Customer.Name\n```\n\nYou can see the name of the customer. The property Customer is an object reference, and you can directly access properties of the referenced object through the dot syntax.\n\n**The referenced object is implicitly reloaded (swizzled) without you having to open it explicitly.**\n\n12. Change the name of the customer:\n\n```\nTRAINING>set a.Customer.Name = \"Mueller, Heinz\"\n```\n\n13. Save the Order\n\n```\nTRAINING>write a.%Save()\n1\n```\n\n14. In the System Management Portal, check the names in the customer table. **_Has the name been changed?_**","Result":"IRIS has changed the customer’s name in the database, although you have \"only\" saved the order. However, since the customer object was opened via the order and was still referenced by the order, when you save the referencing object, all changes in the referenced objects are also saved. This behavior is called **_Deep Save_** and **_Deep Save_** is the default when saving. \n\nSee the documentation for more options on %Save()."},{"Content":"15. **_How can you get the id of the customer starting from the order?_**","Result":"```\nTRAINING>write a.Customer.%Id()\n```"},{"Content":"## Exercise 6 – Relationships\n\nIn this exercise, you will link the **_Training.Order_** and **_Training.OrderPositions_** classes. A parent-child relationship is used for linking because this is a dependent relationship. Relationships in IRIS are\nbidirectional relationships. In each of the classes involved there is a property that represents the relationship. Since all relationships in IRIS are 1:N relationships, a property points to the 1 side and a property points to the N side."},{"Content":"1. In the class Training.Order a property Positions is defined. **_Does this property point to the 1 side or the N side?_**","Result":"The N side"},{"Content":"2. In the Training.OrderPositions class, a property Order is defined. **_What cardinality does it have, 1 or N?_**","Result":"1"},{"Content":"3. Define the property positions in the Training.Order class as follows\n\n~~~objectscript{copy}\nRelationship Positions As Training.OrderPositions(POPSPEC = \":3\") \n\t[ Cardinality = children, Inverse = Order ];\n~~~\n\n4. In the Training.OrderPositions class, the counterpart looks like this:\n\n~~~objectscript{copy}\nRelationship Order As Training.Order \n\t[ Cardinality = parent, Inverse = Positions ];\n~~~\n\n5. Save and compile the classes."},{"Content":"### Excursus – Storage\n\nWhen persistent classes are compiled successfully for the first time, a storage definition is added to them. This is visible in the class definition.\n\n~~~objectscript\nStorage Default\n{\n‹Data name=\"OrderDefaultData\"≥\n\n%%CLASSNAME\n\n\nOrderNr\n\n\nOrderDate\n\n\nCustomer\n\n\n^Training.OrderD\nOrderDefaultData\n‹Idlocation>^Training.OrderD\n‹IndexLocation>^Training.OrderI\n‹StreamLocation>^Training.OrderS\nStorage.Persistent\n}\n~~~\n\nThe storage definition describes in which global `` and at which location in the global the values of the individual properties are stored. If the class definition contains indices or streams, `` and `` specify the globals where the corresponding data is stored.\n\nThe storage definition is automatically updated when the class definition is modified. Manual changes to the storage definition should **ONLY** be made in justified exceptional cases.\n\n**In the case of classes for which data already exists, the storage definition must not be deleted under any circumstances. This can lead to the data no longer being accessible in the desired form and the original storage definition may have to be laboriously restored!**"},{"Content":"1. Look at the **``** in the **Training.Order** and **Training.OrderPositions** classes.\n2. The objects for both classes would currently each be stored in their own global. This isbecause both classes were compiled when the relationship between the two classes was not yet defined. In a parent/child relationship, by default the children are stored in the same global as the parent. To achieve this here, you need to delete the storage definition in both class definitions. Here, this can be done without any problems, since we are still in the development cycle and the data is at most test data that can be recreated.\n3. Delete the storage definition in both class definitions and save and compile the classes again.\n4. Look at the storage definitions again. **Has anything changed?**\n\nThis completes our model for now. In the following exercises we will generate data for the complete model and then work with the data."},{"Content":"## Exercise 7 – Filling the model with data\n\nSince our classes all inherit from **_%Populate,_** they support the data generator in InterSystems IRIS.\n\nIn this exercise we write a class **_Training.Main_** and in this class we implement a method **_setup()_** which can be used to do a reset of the data and rebuild the test data."},{"Content":"1. Create a class **_Training.Main_** and define it as **_Abstract_**. **_What does Abstract mean in this context?_**","Result":"That you can **not** create an object of this class."},{"Content":"2. Add a class method **_setup_** to the class with a parameter **_pAmount As %Integer_** and a return type **_%Status_**. In the following figure you can see an example of a skeleton of this method.\n\n~~~objectscript{copy}\nClassMethod setup(pAmount As %Integer) As %Status\n{\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n\n try {\n\t// TODO\n }\n catch tEx {\n write !,tEx.DisplayString()\n set tSc = tEx.AsStatus() \n }\n\n return tSc\n}\n~~~"},{"Content":"3. The next step is to add code for filling the model with data. Our method should also perform a cleanup of any existing data before creating it. For this we use the **_%KillExtent()_** method.\n\n**_CAUTION!!! %KillExtent()_** does not take into account any constraints that may be present and should therefore only be executed in the context of development work. For deleting data in production environments other methods are available. These are the methods %DeleteId(...) or also a delete ... from ... as SQL statement.\n\n4. Implement the **_setup()_** method by applying the **_%KillExtent()_** and **_Populate()_** methods for the classes. Note that there are dependencies between the classes that affect the order of the Populate calls. For example, the order class contains a reference to the customer class and in order for this reference to be populated correctly, the populate for the customer class must occur before the populate for the order class. Other dependencies exist between OrderPositions and Article and Order and OrderPositions. **_What is the correct sequence to ensure that the model is completely filled with data?_**","Result":"~~~objectscript\nClassMethod setup(pAmount As %Integer) As %Status\n{\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n\n try {\n $$$ThrowOnError(##class(Training.Order).%KillExtent())\n $$$ThrowOnError(##class(Training.OrderPositions).%KillExtent())\n $$$ThrowOnError(##class(Training.Customer).%KillExtent())\n $$$ThrowOnError(##class(Training.Article).%KillExtent())\n set tAmountCustomer = ##class(Training.Customer).Populate(pAmount * 10)\n set tAmountArticle = ##class(Training.Article).Populate(10)\n set tAmountOrder = ##class(Training.Order).Populate(pAmount)\n set tAmountPositions = ##class(Training.OrderPositions).Populate(pAmount)\n write !,tAmountCustomer,\" customers created!\"\n write !,tAmountArticle,\" articles created!\"\n write !,tAmountOrder,\" orders created!\"\n write !,tAmountPositions,\" order positions created!\"\n\n }\n catch tEx {\n write !,tEx.DisplayString()\n set tSc = tEx.AsStatus() \n }\n\n return tSc\n}\n~~~"},{"Content":"5. Execute the **_setup()_** method with an amount of 100.\n6. Look at the data in the System Management Portal via SQL. \n7. Form an SQL join that displays the associated OrderPositions for an order.","Result":"~~~sql\nSELECT \nOrderNr, OrderDate, Article, Quantity, SumTotal, childsub\nFROM Training.OrderPositions as OrderPosition\nINNER JOIN Training.Order as Order \nON OrderPosition.Order = Order.ID\n~~~"},{"Content":"## Exercise 8 – Objects and Relationships\n\nRelationships are accessible via SQL like foreign key relationships. But how can relations be handled via object access?\n\nAs already mentioned, relationships in IRIS are bidirectional relationships. In each of the involved classes there is a property that represents the relationship. We have also already seen that there are two different classes of cardinalities. One/Parent and Many/Children.\n\nIf the cardinality is One/Parent, the object access can be done as with a simple object reference. Also, the Swizzling mechanism works as usual.\n\nIf the cardinality is Many/Children, the relationship property behaves like a collection property and the collection interface is used for access."},{"Content":"1. Determine a valid Orders object ID via SQL\n2. Open the corresponding Orders object with the %OpenId() method\n3. **_Which method can you use to determine the number of positions for this order?_** Use the Class Reference to find the method.","Result":"```objectscript\nTRAINING>set o = ##class(Training.Order).%OpenId(1)\n\nTRAINING>w o.Positions.Count()\n1\n```"},{"Content":"4. **_Which methods can you use to add an order position?_**","Result":"The **`Insert()`** method."},{"Content":"5. Create a new order position and link it to the order. **_What are the two ways to do this?_**","Result":"```objectscript\nTRAINING>set o = ##class(Training.Order).%OpenId(1)\n\nTRAINING>set op = ##class(Training.OrderPositions).%New()\n```\n\n1. way:\n\n```objectscript\nTRAINING>write o.Positions.Insert(op)\n1\n```\n\n2. way:\n\n```objectscript\nTRAINING>set op.Order = o\n```"},{"Content":"## Exercise 9 – Instancemethod\n\n1. Create an instance method **_ListPositions_** in the class **_Training.Order_** , which outputs the individual positions with Article name, Quantity, MarketPrice, Total amount via the object access.\n2. Test the method in the terminal.","Hint":"You can use this skeleton:\n\n~~~objectscript\nMethod ListPositions()\n{\n \n #dim tEx as %Exception.SystemException\n\n try {\n for i=1:1:..Positions.Count() {\n // TODO\n }\n\n }\n catch tEx {\n write !, tEx.DisplayString()\n }\n}\n~~~","Result":"~~~objectscript\nMethod ListPositions()\n{\n \n #dim tEx as %Exception.SystemException\n\n try {\n for i=1:1:..Positions.Count() {\n set tPos = ..Positions.GetAt(i)\n write !,tPos.Article.ArticleDescription,\" \",tPos.Quantity,\" \",tPos.Article.MarketPrice,\" \",tPos.SumTotal\n }\n\n }\n catch tEx {\n write !, tEx.DisplayString()\n }\n}\n~~~"},{"Content":"## Exercise 10 – Retrieve the order data\n\n1. In the **_Training.Main_** class, write a class method **_GetOrderData(pId As %Integer)_** that gets the order data consisting of\n\n- a. customer name\n- b. City of the customer\n- c. Order date\n- d. List of order positions\n\nis displayed. The method is passed the ID of the order and returns the status.\n\n**Use the `OrderDateLogicalToDisplay()` method to display the date!**\n\n2. Compile and test the method.","Hint":"Use: `order.OrderDateLogicalToDisplay(order.OrderDate)`","Result":"~~~objectscript\nClassMethod GetOrderData(pId As %Integer) As %Status\n{\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n\n try {\n set tOrder = ##class(Training.Order).%OpenId(pId,,.tSc)\n $$$ThrowOnError(tSc)\n write !,\"Customer: \",tOrder.Customer.Name,\" \",tOrder.Customer.Address.City\n write !,\"OrderDate: \",tOrder.OrderDateLogicalToDisplay(tOrder.OrderDate)\n do tOrder.ListPositions()\n }\n catch tEx {\n write !,tEx.DisplayString()\n\n }\n\n return tSc\n}\n~~~"},{"Content":"3. **_What stands out about the order date?_**","Result":"It is formated."},{"Content":"4. **_How can you display the date in a readable format?_**","Result":"With the **`OrderDateLogicalToDisplay()`** method."},{"Content":"## Exercise 11 – Class Queries and %SQL.* classes\n\nIn the following exercises you will get to know different classes of SQL queries. A distinction is made between static and dynamic SQL. The static SQL can be defined as a class query or used as embedded SQL directly in ObjectScript."},{"Content":"In this exercise, we will start with class queries.\n\n1. Define in the class **Training.Order** a query **OrderAndCustomer** as shown below:\n\n~~~objectscript{copy}\nQuery OrderAndCustomer() As %SQLQuery [ SqlProc ]\n{\n select OrderDate, OrderNr, Customer->Name,Customer->Address_City from Training.Order\n}\n~~~\n\n2. **_What do the Customer->Name and Customer->Address_City elements in the select list mean?_**\n3. Save and compile the Order class.","Result":"They map the column to the property of the child class."},{"Content":"To execute the class, classes from the **_%SQL_** package are needed.\n\n4. Look at the **_%SQL.Statement_** , **_%SQL.StatementResult_** and **_%SQL.StatementMetadata_** in the [Class Reference](http://$$HOST$$:$$PORT$$/csp/documatic/%25CSP.Documatic.cls). **_What methods are needed to execute a class query?_**","Result":"- `%SQL.Statement.%PrepareClassQuery()`\n- `%SQL.Statement.%Execute()`\n- `%SQL.StatementResult.%GetMetadata()`\n- `%SQL.StatementResult.%Next()`\n- `%SQL.StatementResult.%GetData()`\n- `%SQL.StatementMetadata.GetAt()`"},{"Content":"5. In the **_Training.Main_** class, implement a **_RunClassQuery_** class method that executes the **_OrderAndCustomer_** query and displays the results.\n6. Test the method.","Hint":"You can use **`tStmnt.%PrepareClassQuery(\"Training.Order\",\"OrderAndCustomer\")`** to call the class query, where _tStmnt_ is an object of type **_%SQL.Statement_**.","Result":"~~~objectscript\nClassMethod RunClassQuery(pId As %Integer) As %Status\n{\n #dim tStmnt as %SQL.Statement\n #dim tRs as %SQL.StatementResult\n #dim tMeta as %SQL.StatementMetadata\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n\n try {\n set tStmnt = ##class(%SQL.Statement).%New()\n set tStmnt.%SelectMode = 1\n $$$ThrowOnError(tStmnt.%PrepareClassQuery(\"Training.Order\",\"OrderAndCustomer\"))\n set tRs = tStmnt.%Execute()\n set tMeta = tRs.%GetMetadata()\n\n write !\n for i=1:1:tMeta.columnCount {\n write tMeta.columns.GetAt(i).colName,$c(9)\n }\n write !\n while tRs.%Next() {\n write !\n for i=1:1:tMeta.columnCount {\n write tRs.%GetData(i),$c(9)\n }\n }\n set tRs = \"\"\n set tStmnt = \"\"\n set tMeta = \"\"\n\n }\n catch tEx {\n do tEx.DisplayString()\n set tSc = tEx.AsStatus()\n }\n\n return tSc\n}\n~~~"},{"Content":"7. **_What does the statement set tStmnt.%SelectMode = 1 do?_**","Result":"It sets the _ODBC mode_."},{"Content":"8. Modify the query and the method so that you can use the ID of the order to restrict the result.","Hint":"Use a **WHERE** clause.","Result":"~~~objectscript\nQuery OrderAndCustomer(pId As %Integer) As %SQLQuery [ SqlProc ]\n{\n select OrderDate, OrderNr, Customer->Name,Customer->Address_City from Training.Order\n where id = :pId\n}\n~~~"},{"Content":"The parameter `pId` is used within the SQL statement as a so-called host variable. To\ndistinguish host variables from field names, host variables are preceded by a colon.\n\nThe current parameter is passed to the query in the %Execute method.\n\n~~~objectscript{copy}\nClassMethod RunClassQuery(pId As %Integer) As %Status\n{\n #dim tStmnt as %SQL.Statement\n #dim tRs as %SQL.StatementResult\n #dim tMeta as %SQL.StatementMetadata\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n\n try {\n set tStmnt = ##class(%SQL.Statement).%New()\n set tStmnt.%SelectMode = 1\n $$$ThrowOnError(tStmnt.%PrepareClassQuery(\"Training.Order\",\"OrderAndCustomer\"))\n set tRs = tStmnt.%Execute(pId)\n set tMeta = tRs.%GetMetadata()\n\n write !\n for i=1:1:tMeta.columnCount {\n write tMeta.columns.GetAt(i).colName,$c(9)\n }\n write !\n while tRs.%Next() {\n write !\n for i=1:1:tMeta.columnCount {\n write tRs.%GetData(i),$c(9)\n }\n }\n set tRs = \"\"\n set tStmnt = \"\"\n set tMeta = \"\"\n\n }\n catch tEx {\n do tEx.DisplayString()\n set tSc = tEx.AsStatus()\n }\n\n return tSc\n}\n~~~"},{"Content":"## Exercise 12 – embedded SQL\n\nIn this exercise, embedded SQL is used to execute static queries. A distinction must be made here between cursor-based SQL and non-cursor-based SQL.\n\nWe start with the SQL statement from exercise 11. Embedded SQL now means that the SQL\nstatement can be embedded in ObjectScript. To do this, the SQL statement must be syntactically embedded in `&sql()`. Host variables play an important role in embedded SQL. Parameters are passed to the statement in the form of host variables in the same way as in Exercise 11, but the results are also written to host variables in the INTO clause.\n\nThe statement from exercise 11 thus changes to:\n\n~~~objectscript{c}\n&sql(\n select OrderDate, OrderNr, Customer->Name,Customer->Address_City \n into :tOrderDate, :tOrderNr, :tCustomerName, :tCustomerCity \n from Training.Order\n where id = :pId\n )\n~~~"},{"Content":"1. Write a class method with the following signature **_RunEmbeddedSQL(pId as %Integer) as %Status_** in the **_Training.Main_** class. The method should execute the above embedded SQL statement and display the result. Since the query will return at most one result row due to the constraint over the ID, we do not need cursor-based SQL here.","Result":"~~~objectscript\nClassMethod RunEmbeddedSQL(pId As %Integer) As %Status\n{\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n #SQLCompile Select = ODBC\n try {\n &sql(\n select OrderDate, OrderNr, Customer->Name,Customer->Address_City \n into :tOrderDate, :tOrderNr, :tCustomerName, :tCustomerCity \n from Training.Order\n where id = :pId\n )\n \n write !,tOrderDate,\" \",tOrderNr,\" \",tCustomerName,\" \",tCustomerCity\n\n }\n catch tEx {\n do tEx.DisplayString()\n set tSc = tEx.AsStatus()\n }\n\n return tSc\n}\n~~~"},{"Content":"~~~objectscript{copy}\nClassMethod RunEmbeddedSQL(pId As %Integer) As %Status\n{\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n #SQLCompile Select = ODBC\n try {\n &sql(\n select OrderDate, OrderNr, Customer->Name,Customer->Address_City \n into :tOrderDate, :tOrderNr, :tCustomerName, :tCustomerCity \n from Training.Order\n where id = :pId\n )\n if SQLCODE = 0 {\n write !,tOrderDate,\" \",tOrderNr,\" \",tCustomerName,\" \",tCustomerCity\n }\n else {\n write !,\"Order with ID \",pId,\" not found!\"\n }\n\n }\n catch tEx {\n do tEx.DisplayString()\n set tSc = tEx.AsStatus()\n }\n\n return tSc\n}\n~~~\n\n2. Look at the code. After the SQL statement a variable SQLCODE is used. This variable is set when the SQL statement is executed and indicates whether the execution of the statement was successful (SQLCODE = 0).\n\n**Attention:** If you use an INTO clause in select statements, the host variables in the INTO clause have a defined value only if the SQLCODE = 0.\n\n3. Consider the preprocessor statement **_#SQLCompile Select = ODBC_**. **_What does it do?_**","Result":"Formats data for presentation via ODBC or JDBC."},{"Content":"## Exercise 13 – Cursor based embedded SQL\n\nIn this exercise, the query from Exercise 12 is modified to require an embedded SQL with cursor to execute."},{"Content":"1. Write a class method with the following signature: **_RunEmbeddedSQLWithCursor() As %Status_** in the class **_Training.Main_**.\n2. Modify the SQL statement from Exercise 12 to remove the WHERE clause.\n3. To define a cursor for embedded SQL, the statement must be preceded by **`declare cursor for`**","Result":"~~~objectscript\n&sql(\n declare ptr cursor for\n select OrderDate, OrderNr, Customer->Name,Customer->Address_City into \n :Date, :Number, :Name, :City \n from Training.Order\n )\n~~~"},{"Content":"~~~objectscript{copy}\n&sql(\n declare ptr cursor for\n select OrderDate, OrderNr, Customer->Name,Customer->Address_City into \n :Date, :Number, :Name, :City \n from Training.Order\n )\n~~~\n\nA cursor with the name ptr is defined here. To be able to use the cursor, the cursor\nmust be opened. After a successful opening (SQLCODE = 0), fetch operations can be\nused to iterate over the result quantity. This is analogous to the %Next() method in theResultSet classes. The fetch operation returns the variable SQLCODE = 0 if there are more result rows. Afterwards the cursor must be closed.\n\n4. Rewrite the method implementation.","Hint":"~~~objectscript\nClassMethod RunEmbeddedSQLWithCursor() As %Status\n{\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n #SQLCompile Select = ODBC\n\n try {\n &sql(\n declare ptr cursor for\n select OrderDate, OrderNr, Customer->Name,Customer->Address_City into \n :Date, :Number, :Name, :City \n from Training.Order\n )\n &sql(open ptr)\n if SQLCODE=0 {\n &sql(fetch ptr)\n while SQLCODE = 0 {\n // TODO\n &sql(fetch ptr)\n }\n &sql(close ptr)\n }\n\n }\n catch tEx {\n do tEx.DisplayString()\n set tSc = tEx.AsStatus()\n }\n\n return tSc\n}\n~~~","Result":"~~~objectscript\nClassMethod RunEmbeddedSQLWithCursor() As %Status\n{\n #dim tSc as %Status = $$$OK\n #dim tEx as %Exception.SystemException\n #SQLCompile Select = ODBC\n\n try {\n &sql(\n declare ptr cursor for\n select OrderDate, OrderNr, Customer->Name,Customer->Address_City into \n :Date, :Number, :Name, :City \n from Training.Order\n )\n &sql(open ptr)\n if SQLCODE=0 {\n &sql(fetch ptr)\n while SQLCODE = 0 {\n write !,Date,$c(9),Number,$c(9),Name,$c(9),City\n &sql(fetch ptr)\n }\n &sql(close ptr)\n }\n\n }\n catch tEx {\n do tEx.DisplayString()\n set tSc = tEx.AsStatus()\n }\n\n return tSc\n}\n~~~"},{"Content":"Finally, there is the execution of dynamic SQL. In this context, dynamic SQL means that the structure of the SQL statement is defined at runtime. For the execution of dynamic SQL, the %SQL.* classes are used again and the execution is very similar to the execution of class queries (Exercise 11). The difference here is the call to the **_%Prepare_** method. This is used to prepare SQL statements for execution."},{"Content":"## Exercise 14 – Dynamic SQL\n\n1. In the Class Reference, examine the %SQL.Statement class. **_Which methods are necessary to execute a SQL statement?_**","Result":"- **%Prepare()**\n- **%Execute()**"},{"Content":"2. In the Training.Main class, write a class method with the following signature: **_RunDynamicSQL() As %Status_**. The method should execute the SQL statement from Exercise 13 (without a declare cursor, of course) and display the results.","Hint":"~~~objectscript\nClassMethod RunDynamicSQL() As %Status\n{\n#dim tSql as %String = \"select OrderDate, OrderNr, Customer->Name,Customer->Address_City from Training.Order\"\n#dim tStmnt as %SQL.Statement = ##class(%SQL.Statement).%New()\n#dim tRs as %SQL.StatementResult\n#dim tMeta as %SQL.StatementMetadata\n#dim tEx as %Exception.SystemException\n#dim tSc as %Status = $$$OK\n\n try {\n set tStmnt.%SelectMode = 1\n $$$ThrowOnError(tStmnt.%Prepare(tSql))\n set tRs = tStmnt.%Execute()\n set tMeta = tRs.%GetMetadata()\n write !\n for i=1:1:tMeta.columnCount {\n write tMeta.columns.GetAt(i).colName,$c(9)\n }\n \n while tRs.%Next() {\n //TODO\n }\n set tRs = \"\"\n set tStmnt = \"\"\n set tMeta = \"\"\n }\n catch tEx {\n do tEx.DisplayString()\n set tSc = tEx.AsStatus()\n }\n\n return tSc\n}\n~~~","Result":"~~~objectscript\nClassMethod RunDynamicSQL() As %Status\n{\n#dim tSql as %String = \"select OrderDate, OrderNr, Customer->Name,Customer->Address_City from Training.Order\"\n#dim tStmnt as %SQL.Statement = ##class(%SQL.Statement).%New()\n#dim tRs as %SQL.StatementResult\n#dim tMeta as %SQL.StatementMetadata\n#dim tEx as %Exception.SystemException\n#dim tSc as %Status = $$$OK\n\n try {\n set tStmnt.%SelectMode = 1\n $$$ThrowOnError(tStmnt.%Prepare(tSql))\n set tRs = tStmnt.%Execute()\n set tMeta = tRs.%GetMetadata()\n write !\n for i=1:1:tMeta.columnCount {\n write tMeta.columns.GetAt(i).colName,$c(9)\n }\n \n while tRs.%Next() {\n write !\n for i=1:1:tMeta.columnCount {\n write tRs.%GetData(i),$c(9)\n }\n }\n set tRs = \"\"\n set tStmnt = \"\"\n set tMeta = \"\"\n }\n catch tEx {\n do tEx.DisplayString()\n set tSc = tEx.AsStatus()\n }\n\n return tSc\n}\n~~~"},{"Content":"3. Save and compile the class and test the method. \n4. Examine the code. What does the statement **`set tStmnt.%SelectMode = 1`**?","Result":"Sets the _ODBC mode_."},{"Content":"### Summary\n\n?[sql-types-summary.png](sql-types-summary.png){width:100%}"},{"Content":"## Exercise 15 – REST Specification First\n\nIn this exercise, you will create a REST service that returns custom data. You will use the Specification First option to create the framework of the REST service from a specification. The REST service will use JSON as the message format. InterSystems IRIS supports JSON inbound and outbound out of the box. Analogous to the XML Support, the **_%JSON.Adaptor_** class is added to the list of super classes for the JSON Support as another superclass."},{"Content":"In addition, there are two more classes that support JSON handling and are very easy and intuitive to use. The two classes are **_%DynamicObject_** and **_%DynamicArray_**. With **_%DynamicObject_** objects can be created on-the-fly without prior class definition and exported to JSON or JSON strings can be received and converted to objects. The same is true for **_%DynamicArray_**.\n\n1. In the terminal, create a dynamic object with **_FirstName, LastName_** , and **_City_** properties and set appropriate values in the properties.\n2. Output this object as JSON.\n","Hint":"Use the **%ToJSON()** method, that returns the a JSON String from a DynamicObject.","Result":"```\nTRAINING>set obj = ##class(%DynamicObject).%New()\nTRAINING>do obj.%Set(\"FirstName\",\"John\")\nTRAINING>do obj.%Set(\"LastName\",\"Smith\")\nTRAINING>do obj.%Set(\"City\",\"London\")\nTRAINING>write obj.%ToJSON()\n{\"FirstName\":\"John\",\"LastName\":\"Smith\",\"City\":\"London\"}\n```"},{"Content":"3. In the Class Reference, load the [documentation](http://$$HOST$$:$$PORT$$/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25JSON.Adaptor) for **_%JSON.Adaptor_** and familiarize yourself with the JSON Adaptor.\n4. Add **_%JSON.Adaptor_** to the list of super classes in the **_Training.Customer_** and **_Training.Address_** classes.\n//5. There is a file API-Spec on your desktop. This file contains a Swagger specification about the REST service you want to implement. Open the file and look at the contents. **_What is the name of the endpoint to be implemented?_**\n5. Download the API-Spec. This file contains a Swagger specification about the REST service you want to implement. Open the file and look at the contents. **_What is the name of the endpoint to be implemented?_**\n\n$$$[Download API-Spec](API-Spec.json)","Hint":"Take a look at the _paths_ property.","Result":"`/Customer/{id}`"},{"Content":"6. **_Which http method should be used to call the endpoint?_**","Hint":"Take a closer look at the specified JSON object for the _`/Customer/{id}`_ path.","Result":"GET"},{"Content":"7. **_Does the endpoint have a parameter?_**","Hint":"Take a look at the _parameters_ property.","Result":"Yes the _id_ parameter in the path to identify the costumer."},{"Content":"8. InterSystems IRIS provides a REST API for API management. One of these endpoints can be used to load Swagger specifications and generate the required classes for implementing the REST service in InterSystems IRIS based on the specification.\n\nThe endpoint for this case is:\n[http://$$HOST$$:$$PORT$$/api/mgmnt/v2/TRAINING/Training](http://$$HOST$$:$$PORT$$/api/mgmnt/v2/TRAINING/Training)\n\nThis endpoint specifies as the first parameter the namespace in which the classes should be generated. In our case it is the namespace TRAINING. The second parameter is a name for the application to be generated. In this case also Training.\n\n9. The endpoint is called with the POST method and in the body of the request the Swagger specification is passed."},{"Content":"10. Start [Postman](https://www.postman.com/downloads/).\n11. In the URL input field, enter the above URL and copy the Swagger specification from the file into the body of the request. Select POST as the method.\n12. Under _Authorization_ select _**Basic Auth**_ and enter your credentials.","Result":"\n?[PostmanApiSpec.jpg](PostmanApiSpec.jpg){width:60%}"},{"Content":"13. Send the request. You will receive the following response\n\n~~~json\n{\n \"msg\": \"New application Training created\"\n}\n~~~"},{"Content":"14. The REST request creates three classes on the server.\n\n- **_a. Training.disp.cls_**\n- **_b. Training.spec.cls_**\n- **_c. Training.impl.cls_**\n\nThe **Training.disp.cls** class serves as the dispatcher. **Training.spec.cls** contains the\nspecifications and Training.impl.cls the implementation.\n\n15. If you are working with VSCode add the TRAINING Namespace again to your Worspace and make sure to enable the Checkbox _Show Generated_ in the Filter options\n16. Look at the generated classes. In the dispatcher class, there is an **_XData_** block **_UrlMap_** that is responsible for forwarding incoming requests. Here you can see that incoming requests for the Customer endpoint are forwarded to the **_GetCustomer_** method. If you look at the implementation of the **_GetCustomer_** method, you can see that the method calls a **GetCustomer** method in the **_Training.impl_** class."},{"Content":"17. Open the **_Training.impl_** class. A **_GetCustomer_** method is already created in the class. The implementation code is still missing.\n18. Implement the **_GetCustomer_** method using the **_%JSON.Adaptor_** class and **_%DynamicObject_**.","Hint":"USe the method **`%JSONExportToStream(.tReturn)`** from the %JSON.Adapter to format a object to a JSON String.","Result":"~~~objectscript\nClassMethod GetCustomer(id As %String) As %Stream.Object\n{\n try {\n set %response.ContentType = \"application/json\"\n set tCustomer = ##class(Training.Customer).%OpenId(id,,.tSc)\n $$$ThrowOnError(tSc)\n set tSc = tCustomer.%JSONExportToStream(.tReturn)\n }\n catch tEx {\n set tReturn = {\"Status\":(tEx.DisplayString())} \n }\n Do ..%SetStatusCode(200)\n \n return tReturn\n}\n~~~"},{"Content":"To make the REST service available, one more step is missing. A web application must be created in System Management Portal to activate the REST service.\n\n19. Open the System Management Portal from the IRIS Cube.\n20. Go to **_[System Administration > Security > Applications > Web Applications](http://$$HOST$$:$$PORT$$/csp/sys/sec/%25CSP.UI.Portal.Applications.WebList.zen)_**\n21. Select **_Create new web application._**\n22. In the **_Name_** field please enter **_/api/training/v1_**\n23. In the **_Namespace_** combo box select the namespace **_Training_**\n24. Activate the radio button **_REST._**\n25. In the field Dispatch Class enter **_Training.disp_**\n25. Click **_Save_**\n"},{"Content":"The REST service should now be ready for use.\n\n27. Test the service with Postman by entering the following URL\n\n\n[http://$$HOST$$:$$PORT$$/api/training/v1/Customer/2](http://$$HOST$$:$$PORT$$/api/training/v1/Customer/2)\n\n28. Use the GET method.\n29. **_What happens if you enter an invalid ID?_**","Result":"A JSON object with a _status_ property and the status message as value will be returned."},{"Content":"## Exercise 16 – Interoperability\n\nIn the exercise, you will learn about the Interoperability Layer of InterSystems IRIS. The interoperability layer offers you the possibility to communicate bidirectionally with external systems and to map and implement cross-system processes. Rule-based routing of messages is also possible."},{"Content":"Interoperability applications are organized in so-called productions. A production is based on three pillars:\n\n- Business services (input side)\n- Business Processes and Routers\n- Business Operations (output side)\n\n?[IRISPRoductionOverview.jpg](IRISPRoductionOverview.png){width:50%}\n\nThe three components Business Services, Business Processes and Business Operations communicate\nwith each other via messages. These messages are objects that are sent from one component to the\nnext. The basic message flow within a production is from left to right. That is, from the business\nservice as input to the business process and then to the business operation as output.\n\n\nBusiness services and business operations can additionally use adapters. An adapter provides the\ntechnical bridge to an external system (SMTP, POP3, FTP, Filesystem, REST, SOAP, ...) and the\ndeveloper does not have to worry about how to communicate with the external system.\n"},{"Content":"In the following exercise, you will create a small production that uses a simple business process to create a new customer in the database. To do this, you will need four components:\n\n- A production\n- A message class\n- A business process\n- A business operation"},{"Content":"1. Create a new production in the System Management Portal. To do this, go to the menu **_[Interoperability > Configure > Production](http://$$HOST$$:$$PORT$$/csp/training/EnsPortal.ProductionConfig.zen?$NAMESPACE=TRAINING&$NAMESPACE=TRAINING&)_**\n2. The production overview appears. Select **_New_**\n\n?[ProductionWizard.png](ProductionWizard.png){width:60%}\n\n3. Select **_Training_** as package.\n4. Select **_Production_** as the production name.\n5. Click **_OK_**\n"},{"Content":"### Exercise 16.1 – Messages\n\nMessage classes are persistent classes with the superclass **_Ens.Request_** for request messages and **_Ens.Response_** for response messages.\n\n1. Create a class **Training.MSG.CustomerRequest** that inherits from **_Ens.Request_**.\n2. Add the properties **_Name_** (%String), **_CustomerNumber_** (%Integer), **_City_** (%String), **_Postcode_** (%Integer) and **_Street_** (%String) to your message class.","Result":"~~~objectscript\nClass Training.MSG.CustomerRequest Extends Ens.Request\n{\n\nProperty Name As %String;\n\nProperty CustomerNumber As %Integer;\n\nProperty City As %String;\n\nProperty Postcode As %Integer;\n\nProperty Street As %String;\n\n}\n~~~"},{"Content":"3. Now write a response message class **_Training.MSG.CustomerResponse_**. This class has only one property **_Status_** (%Status).","Hint":"Now you should inherit from **_Ens.Response_**.","Result":"~~~objectscript\nClass Training.MSG.CustomerResponse Extends Ens.Response\n{\n\nProperty Status As %Status;\n\n}\n~~~"},{"Content":"### Exercise 16.2 – Business Operation\n\nThe next step is to create a business operation that takes as input a Training.MSG.CustomerRequest message, stores the customers in the database, and returns a status information as Training.MSG.CustomerResponse.\n\n1. Create a class **Training.BO.Customer** that inherits from **_Ens.BusinessOperation_**.\n2. Add a Parameter _INVOCATION_ to your class and set it to the value \"Queue\".\n3. Add a instance method called **_InsertCustomer_** that returns a **%Status** and has this parameters:\n - **pRequest** of type _Training.MSG.CustomerRequest_\n - **pResponse** of type _Training.MSG.CustomerResponse_ as an Output","Hint":"You can define a parameter as output like this:\n\n~~~objectscript\nMethod MyMethod(Output param as %String){\n...\n}\n~~~","Result":"~~~objectscript\nClass Training.BO.Customer Extends Ens.BusinessOperation\n{\n\nParameter INVOCATION = \"Queue\";\n\nMethod InsertCustomer(pRequest As Training.MSG.CustomerRequest, Output pResponse As Training.MSG.CustomerResponse) As %Status\n{\n\treturn $$$OK\n}\n\n}\n~~~"},{"Content":"4. Now add this code to your class:\n\n~~~objectscript{copy}\nXData MessageMap\n{\n\n\t \n\t\tInsertCustomer\n\t\n\n}\n~~~\n\nThis describes whenever the operation receives a request of type _Training.MSG.CustomerRequest_ the method **InsertCustomer** will be called.\n\n5. Compile the class.\n\n\n","Result":"~~~objectscript\nClass Training.BO.Customer Extends Ens.BusinessOperation\n{\n\nParameter INVOCATION = \"Queue\";\n\nMethod InsertCustomer(pRequest As Training.MSG.CustomerRequest, Output pResponse As Training.MSG.CustomerResponse) As %Status\n{\n\n\treturn $$$OK\n}\n\nXData MessageMap\n{\n\n\t \n\t\tInsertCustomer\n\t\n\n}\n\n}\n\n~~~"},{"Content":"In the next step, implement the Insert Customer method.\n\n6. Look at the signature of the InsertCustomer method. The method is passed an object of type Training.MSG.CustomerRequest and returns an output object of type Training.MSG.CustomerResponse. I.e. you have to create and populate this response object in your code.\n7. Implement the **_InsertCustomer_** method, so that a new customer is created whenever a CustomerRequest is received and the status is returned inside a _CustomerResponse_.","Hint":"To return a _CustomerResponse_ you have **not** to return a _CustomerResponse_. Because _pResponse_ is a Output you can just create a new object of type _CustomerResponse_ and assign it to _pResponse_","Result":"~~~objectscript\nMethod InsertCustomer(pRequest As Training.MSG.CustomerRequest, Output pResponse As Training.MSG.CustomerResponse) As %Status\n{\n\n\t#dim tCustomer as Training.Customer\n\t#dim tEx as %Exception.SystemException\n\t#dim tSc as %Status = $$$OK \n\t\n\ttry {\n\t\tset pResponse = ##class(Training.MSG.CustomerResponse).%New()\n\t\tset tCustomer = ##class(Training.Customer).%New()\n\t\tset tCustomer.CustomerNr = pRequest.CustomerNumber\n \tset tCustomer.Name = pRequest.Name\n \tset tCustomer.Address.Street = pRequest.Street\n \tset tCustomer.Address.Postcode = pRequest.Postcode\n \tset tCustomer.Address.City = pRequest.City\n\t\t$$$ThrowOnError(tCustomer.%Save())\n\t\tset pResponse.Status = 1\n\t} catch tEx {\n\t\tset pResponse.Status = tEx.AsStatus()\n\t\tset tSc = tEx.AsStatus()\n\t}\n\treturn tSc\n}\n~~~"},{"Content":"In the next step, add the new business operation to the production.\n\n8. Switch to the Production overview in the System Management Portal. Click on the plus sign next to the operations.\n9. Select the class of the new business operation. The business operation can be given a logical name in production. Enter **_InsertCustomer_** as the name.\n10. Click the **_Activate now_** checkbox."},{"Content":"The production overview now looks like this:\n\n?[MyFirstOperation.png](MyFirstOperation.png){width:60%}\n\n11. Before you start and test the production, click the Production Settings link above the operations. In the right pane with the **_production settings_** , expand the **_Development and Debugging_** section.\n12. Check the **_Test enabled_** checkbox and click **_Apply_**.\n13. Start the production by clicking **_Start_**. The Business Operation should be displayed in green.\n14. Next, test the business operation. To do this, click on the business operation and then activate the **_Actions_** tab in the right pane.\n15. Select **_Test_**\n16. In the dialog that appears, you will be prompted to populate the Request object. Enter your test data here and then click on **_Call the test service_**.\n17. **_Which response message do you get back?_**\n18. Click on **_Visual Trace_** and view the message flow. The message trace is automatically generated for each message that passes through your production and makes the flow of messages traceable. This information also helps in case the production does not work as expected."},{"Content":"### Exercise 16.3 – Business Process\n\nIn the next step, you will extend the production with a simple business process that uses the\nbusiness operation. _BPEL_ (Business Process Execution Language) will be used to define the business\nprocess. You will use the System Management Portal to define the Business Process."},{"Content":"1. In the System Management Portal go to the menu **_Interoperability_** and from there to the menu **_Build_** and then **_[Business Processes](http://$$HOST$$:$$PORT$$/csp/training/EnsPortal.BPLEditor.zen?$NAMESPACE=TRAINING&$NAMESPACE=TRAINING&)_**.\n2. You will get an empty process diagram with only a start and end point.\n3. In the **_Add Activity_** box, select a **_call_** element. An empty Call element appears in the diagram. You can place this on the diagram using drag and drop or arrange it using the toolbar.\n4. Connect the **_Start_** and **_End_** nodes to the Call element by \"dragging\" the connection node. The borders of Start and End nodes get a blue color, the border of the **_Call_** element remains red. This is a sign that the call element needs more information, and the process cannot be compiled like this.","Result":"\n?[MyFirstBusinessProcess0.png](MyFirstBusinessProcess0.png){width:30%}"},{"Content":"5. Select the **_call_** element.\n6. Select the **_Context_** tab in the right pane. Here you define the context of the businessprocess. I.e., which request class and which response class is assigned to the business process. But also, other information that you want to store in the business process, you can create here in the process context. In this simple example, the business process should use the same request and response classes as the business operation. In the **_Request Class_** and **_Response Class_** boxes, select the appropriate **_Training.MSG.CustomerRequest_** and **_Training.MSG.CustomerResponse_** message classes by clicking on the magnifying glass.\n7. In the right pane, activate the **_Activity_** tab.\n8. In the Name field enter **_InsertCustomer._**\n9. At the Destination field, click the magnifying glass and select Business Operation **_InsertCustomer_** in the dialog that appears. Click **_OK_**."},{"Content":"10. Next, you need to populate Request and Response of the call. Click the magnifying glass to the right of the **_Request message class_** field. In the dialog that appears, select class **_Training.MSG.CustomerRequest_** and click **_OK_**.\n11. Click the plus sign to the right of **_Request Actions_**.\n12. This is where the request object ( **_callrequest_** ) for calling the business operation is populated. Since the Business process structurally uses the same request class ( **_request_** ), you can simply assign `callrequest = request` here. Select this accordingly in the dialog that appears. Click **_OK_**.\n13. Select **_Traing.MSG.CustomerResponse_** for the response message class.\n14. Set the response in the same way as in step 11. Note that the assignment here must be the other way around. The business operation returns **_callrepsonse_** and this must be assigned to the business process response **_response_**."},{"Content":"15. Look at the process diagram. The outline of the call element is now also blue, and the process could be compiled. There is still a small addition to be made. Calls are asynchronous by default. I.e., the call is dropped, and the business process continues to run. If you want to continue processing the result of a call in one place, you must add a **_synch_** element to the business process.\n16. Select the connection line between the **_call_** element and the **_end_** node.\n17. Select a **_Synch_** element in the **_Add activity_** box.\n18. Select the **_Synch_** object and activate the **_Activity_** tab in the right pane.\n19. In the **_Calls_** box, select the call **_CustomerEntry_**. This is the call to the business operation and the **_Sync_** object ensures that the process waits for the return of the call to the business operation at this point and that the result can be further processed in the process.\n20. Save the process in the **_Training.BP_** package and give it the name **_CustomerEntry_**.\n21. Compile the process."},{"Content":"22. The last thing to do is to mount the process in production. To do this, go back to the production overview and add your process to the business processes. Give the process the name Process **_CustomerEntry_**. Activate the process with the **_Activate_** now checkbox.\n23. The process appears green in the production overview. Select the process. A connection line to the business operation appears. This indicates that the business process uses the business operation.\n24. Test the process using the **_Test_** action.\n25. View the result and examine the **_Visual Trace_**\n26. **_How is the trace different from the previous one?_**"},{"Content":"### Exercise 16.4 – Business Service\n\nThe next step is to extend the **_production_** with a service that can automatically read CSV files with\ncustomer data from the file system. For this purpose, the already created process must be extended and an **_EnsLib.RecordMap.Service.FileService_** a **_record map_** and a **_data transformation_** are needed,\nwhich are created in the following steps."},{"Content":"1. in the Systems Management Portal, a new record map is created in the **_[Interoperability > Build > Record Maps](http://$$HOST$$:$$PORT$$/csp/training/EnsPortal.RecordMapper.zen?$NAMESPACE=TRAINING&$NAMESPACE=TRAINING&)_** menu.\n2. Click on **_CSV Wizard_** and select a **_sample file._** To do this, download the **_clients.csv_** file:\n\n$$$[Download](clients.csv)\n\n3. Select in the dialog below under **_`File of type: All Files (*)`_** and open the **_clients.csv_** file.\n4. Specify the name **_Training.ClientsRecMap._** Define as separator: **_;_** and set the check mark under **_Sample has header row._**\n\n?[CSVRecordWizard.png](CSVRecordWizard.png){width:50%}"},{"Content":"5. Then click **_Create RecordMap._** The wizard now automatically detects headers and data fields of the CSV file. \n6. Click **_Save_** and **_Generate_** so that the necessary classes are created.\n\n?[MyFirstRecordMap.png](MyFirstRecordMap.png){width:50%}"},{"Content":"In the next step, a data transformation is created. This is needed to map the data from the CSV file to the data model available in IRIS.\n\n7. In the Systems Management Portal, a new record map is created in the menu **_[Interoperability > Build > Data Transformations](http://$$HOST$$:$$PORT$$/csp/training/EnsPortal.DTLEditor.zen?$NAMESPACE=TRAINING&$NAMESPACE=TRAINING&)_**.\n8. Click on **_New_** and select the package **_Training.BP.DTL._** Select **_CustomerRecToMSG_** as the name.\n9. In the right side menu, you can now specify the source and target class under **_Transform._** As the source class, select the **_Record_** class under **_Persistent Classes_** in the **_Training.ClientsRecMap_** package.\n10. As target class, enter **_Training.MSG.CustomerRequest,_** because this is the format that will be used for communication between our process created above and the operation.\n11. Now drag and drop the data fields of the record map to the message class.\n12. Save and compile the transformation as follows:\n\n?[MyFirstDataTransformation.png](MyFirstDataTransformation.png){width:50%}"},{"Content":"To use the data transformation just created, it will be included in our process.\n\n13. Go back to the **_Production,_** select the process, and open it from the settings side menu via the small magnifying glass.\n14. First, in the context, change the request class to **_Training.ClientRecMap.Record_** and the response class to **_Training.MSG.CustomerResponse_**, because now the process is no longer called by a simple request but receives as input a record of our record map.\n15. Define a new context variable of type **_Training.MSG.CustomerRequest_** and give it the name **_CustomerReqObj._** This variable will be used to cache the data from the CSV records and then pass it on to the operation in the **_call activity_**.\n\n?[BPContextDataTransformation.png](BPContextDataTransformation.png){width:50%}"},{"Content":"16. Now modify the process by inserting a **_transform activity_** between Start and the call activity. Enter the name **_Request to MSG object._**\n17. Define as data transformation class the just created data transformation: **_Training.BP.DTL.CustomerRecToMSG_**\n18. Set the source to **_request_** and the target to **_context.CustomerReqObj_**.\n19. The process should now look like this:\n\n?[BusinessProcessAdvanced.png](BusinessProcessAdvanced.png){width:20%}"},{"Content":"20. Finally, the **_call activity_** must be adapted. This now receives the customer request message object transformed by the data transformation and temporarily stored in the context variable, which it forwards to the operation:\n\n?[BusinessProcessAdvanced2.png](BusinessProcessAdvanced2.png){width:50%}"},{"Content":"To read CSV files automatically, we will use **_EnsLib.RecordMap.Service.FileService_** with the record map we just created, as described at the beginning.\n\n21. In the **_Production,_** add a new service/service via the small plus sign next to the services. Select **_EnsLib.RecordMap.Service.FileService_** as the service class. Set the name **_CustomerCsvIn._** You can check the activate now box directly.\n22. In the settings of the service specify the file path: **_`C:\\InterSystems\\CustomerData\\In\\`_**\n23. File Spec: **_`*`_**\n24. Archive path: **_`C:\\InterSystems\\CustomerData\\Archiv\\`_**\n25. RecordMap: **_Training.ClientsRecMap_**\n26. Target configuration name: **_CustomerEntry_**\n27. As soon as the production including all components is started, you can now observe that the CSV file is read in your file system and then moved to the archive path.\n28. Take a look at the message trace for one of the received records."},{"Content":"## Exercise 17 – Embedded Python Basics\n\nEmbedded Python in InterSystems IRIS makes it possible to use Python as an equal language for\nserver-side development. Python and ObjectScript run in the same process and are completely\ntransparent to each other. I.e., you can use ObjectScript methods from Python and vice versa.\n\nIt is also possible to access objects, SQL tables and globals with Python.\n\nIn addition, you can include existing Python modules in your ObjectScript methods etc. and thus\nseamlessly use the huge ecosystem of Python in InterSystems IRIS.\n\nIn this exercises, you will continue to use the data model you have created so far but create\nvarious methods using Python instead of ObjectScript."},{"Content":"To access InterSystems IRIS, you will need to import the InterSystems IRIS kernel. This is done by an Import of the Python module iris.\n\nThis module provides a set of classes and functions that you need to work with InterSystems IRIS from embedded Python.\n\n1. Examine the iris module using the **_help()_** function in Python.\n2. To do this, start a IRIS terminal. At the input prompt, type **_do ##class(%SYS.Python).Shell()_**. This brings you to a Python shell. Recognizable by the following prompt: **_>>>_**\n3. Import the module **_iris_** : **_import iris_**\n4. Display the help for the module **_iris_** : **_help(iris)_**\n5. Examine the output.\n6. **_What is the name of the built-in function that returns a reference to an InterSystems IRIS class?_**\n\n","Result":"**`iris.cls(\"PKG.ClassName\")`**"},{"Content":"7. Have a Python statement display the instancename of your IRIS installation.\n8. **_What is the name of the Python command to do this?_**","Hint":"Have a closer look at the **%SYS.System** class.","Result":"```shell\n>>> system = iris.cls('%SYS.System')\n>>> print(system.GetInstanceName())\nIRIS\n```"},{"Content":"## Exercise 18 – Classes- and Instance Methods in Python\n\nIn this exercise, you will write a class method and an instance method in Python.\n\n1. In the **_Training.Customer_** class, write a class method **_pyCreateCustomer(...)_** that works like the **_CreateCustomer_** method, but has Python as its implementation language.\n2. Consider the following code and try to explain it:\n\n~~~objectscript{copy}\nClassMethod pyCreateCustomer(\tpCustomerNr As %Integer, \n\t\t\t\tpName As %String, \n\t\t\t\tpStreet As %String, \n\t\t\t\tpPostcode As %Integer, \n\t\t\t\tpCity As %String) As %String [ Language = python ]\n{\n import iris\n clsCustomer = iris.cls(\"Training.Customer\")\n tCustomer = clsCustomer._New()\n \n tCustomer.CustomerNr = pCustomerNr\n tCustomer.Name = pName\n tCustomer.Address.Street = pStreet\n tCustomer.Address.Postcode = pPostcode\n tCustomer.Address.City = pCity\n return str(tCustomer._Save()) + \", \" + str(tCustomer._Id())\n}\n~~~"},{"Content":"3. By **_[ Language = python ]_** it is defined that the following method code uses embedded Python. **_How to create a new instance of the Training.Customer class?_**\n4. **_What would the analogous statement be called in ObjectScript?_**","Result":"- _Embedded Python:_ `customer = iris.cls(\"Training.Customer\")._New()` \n- _ObjectScript:_ `customer = ##class(Training.Customer).%New()` "},{"Content":"In embedded Python, for methods or properties whose names begin with %, the % is\nreplaced with an underscore `_`.\n\n5. Implement and test the method at the InterSystems IRIS terminal prompt."},{"Content":"6. In the next step, write an instance method **_pyPrint()_** that prints the name and address of the customer for a customer instance.\n7. Test the method at the terminal prompt.","Hint":"Use the usual Python procedure to access attributes of an object from instance methods.","Result":"~~~objectscript\nMethod pyPrint() [ Language = python ]\n{\n print(self.Name + \" \" + self.Address.Street + \" \" + str(self.Address.Postcode) + \" \" + self.Address.City)\n}\n~~~"},{"Content":"8. To finish this exercise, write a **_pyListPositions()_** method in the **_Training.Order_** class with Embedded Python to list the positions for a given order\n9. Test the method at the terminal prompt.","Hint":"USe this construct:\n\n~~~objectscript\nMethod pyListPositions() [ Language = python ]\n{\n for tIdx in range(1,self.Positions.Count()+1):\n # TODO\n}\n~~~","Result":"~~~objectscript\nMethod pyListPositions() [ Language = python ]\n{\n for tIdx in range(1,self.Positions.Count()+1):\n tPos = self.Postionen.GetAt(tIdx)\n print(tPos.Article.ArticleDescription + \" \" + str(tPos.Quantity) + \" \" \n + str(tPos.Article.MarketPrice) + \" \" + tPos.SumTotal)\n}\n~~~"},{"Content":"## Exercise 19 – Embedded Python and SQL\n\nThe IRIS module contains a class **_sql_**, via which SQL statements can be executed directly. For this purpose, there is the **_exec_** function.\n\n1. In the **_Training.Customer_** class, write a **_pyCustomerList_** method in Python that displays the ID, name, and city of the customer.\n2. Test the method at the terminal prompt.","Hint":"Use: `iris.sql.exec(sql)`","Result":"~~~objectscript\nClassMethod pyCustomerList() [ Language = python ]\n{\n import iris\n\n tSql = \"select id, name, address_city from training.customer\"\n\n tRs = iris.sql.exec(tSql)\n\n for tRow in tRs:\n print(tRow)\n}\n~~~"},{"Content":"## Exercise 20 – Python Objects in Object Script\n\n1. Change the method signature so that the method gets a parameter **_pId_** (Integer) and only the data of the customer with this Id is output. The method should determine the customer data for the ID via SQL and return the result line(Python list).\n","Hint":"Use: `iris.sql.exec(sql, param)`","Result":"~~~objectscript\nClassMethod pyGetCustomer(pId As %Integer) [ Language = python ]\n{\n import iris\n\n tSql = \"select id, name, address_city from training.customer where id = ?\"\n\n tRs = iris.sql.exec(tSql, pId)\n\n tRow = tRs.__next__()\n\n return tRow\n}\n~~~"},{"Content":"2. Take a look at the code. **_Where are the differences from the previous implementation?_**","Result":"- The SQL Statement uses a Parameter\n- It gets the first row with the **`__next__()`** method"},{"Content":"3. Test the method at the terminal prompt. **_How to access the items in the Python list from ObjectScript?_**","Result":"```objectscript\nTEST>s c = ##class(Training.Customer).pyGetCustomer(1)\n\nTEST>for i = 0: 1:c.\"__len__\"()-1{w !,c.\"__getitem__\"(i)}\n```"},{}]}] \ No newline at end of file