Nicholai Mitchko · Apr 23, 2025 go to post

A while back we created JSON2Persistent which does this for you in a consistent way without AI :)

do ##class(ISC.SE.Tools.JSON).GenerateClasses("path/to/sample.json", "MyDataModel.JSONPackage", "ClassName", 0, 1)
// or
do ##class(ISC.SE.Tools.JSON).GenerateClassesFromStream(jsonStream, "MyDataModel.JSONPackage", "ClassName", 0, 1)

// and then use that class

Set mymessage = ##class(MyDataModel.JSONPackage.ClassName).%JSONImport(mysourcestringorstream)
Set mymessage.%Save()

// and it's SQL addressable

SELECT * FROM MyDataModel_JSONPackage.ClassName
Nicholai Mitchko · Mar 16, 2023 go to post

I use this tidbit to get an external ip

curl -s http://whatismyip.akamai.com/

If you want the docker-host ip and not the external ip, I use host networking and then use

hostname -I
Nicholai Mitchko · Feb 24, 2023 go to post

Please ensure that any dark version uses our white logo as the default color is invisible in dark mode.

Nicholai Mitchko · Nov 5, 2022 go to post

In your docker build try this sequence:

USER root
RUN apt-get clean -yq && apt-get update -yq && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends openjdk-11-jdk
USER ${ISC_PACKAGE_MGRUSER}


Or install sudo (very bad practice in containers):

USER root
RUN apt-get clean -yq && apt-get update -yq && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends tzdata curl gnupg ca-certificates sudo
RUN /bin/echo -e ${ISC_PACKAGE_MGRUSER}\\tALL=\(ALL\)\\tNOPASSWD: ALL >> /etc/sudoers
RUN sudo -u ${ISC_PACKAGE_MGRUSER} sudo echo enabled passwordless sudo-ing for ${ISC_PACKAGE_MGRUSER}
USER ${ISC_PACKAGE_MGRUSER}
Nicholai Mitchko · Oct 29, 2022 go to post

Currently, product development is working on containerization and kubernetes support for healthshare. It might be worth waiting until it is officially supported.

Nicholai Mitchko · Oct 28, 2022 go to post

good to know! how does ZPM do the actual pip installation? from pip._internal import pipmain or irispython -m pip install ...

Nicholai Mitchko · Sep 30, 2022 go to post

I think what you'll need is a write stream method below (I haven't had a chance to test this so the code comes with no guarantees). This will work for Non-Shared web-socket connections since I don't have the chance to test with shared pools at the moment.

Method Send(message As %Library.DynamicObject) As %Integer
{
    Set stream = ##class(%Stream.TmpCharacter).%New()
    Do message.Msg.%ToJSON(stream)
    Set sc = ..WriteStream(stream)
    If $$$ISERR(sc) $$$SysLog(2,"WebSocket","[Write] Error WRITE Stream on JSON Command",sc)
    quit 1
}

Method WriteStream(data As %Stream.Object) As %Status
{
	Set $ZTrap="WriteError"
	If i%WSClassProtocolVersion > 1 & i%WSDataFraming = 1 {
		Set head=$ZLChar(data.Size)
		If i%BinaryData = 1 {
			Set head=head_"8"
		} Else {
			Set head=head_"7"
		}
	} Else {
		Set head=""
	}
	#; As there is activity on this session reset the session timeout
	Set sc=$$updateTimeout^%SYS.cspServer(i%SessionId) If $$$ISERR(sc) $$$SysLog(2,"WebSocket","[Write] Error updating session timeout",sc)
	#; Only return an error status if there's an issue with the write itself.
	Set sc=$$$OK
	If (i%SharedConnection = 1) {
        // If you want to try and play with the shared pools and large payloads, move the commented code to the loop below

		#; Set sc=$$CSPGWClientRequest^%SYS.cspServer3(i%GWClientAddress,"WSW "_i%WebSocketID_" "_head_data1,-5,.response)
		#; If $$$ISERR(sc) $$$SysLog(2,"WebSocket","[Write] Error sending request",sc)
        $$$SysLog(2,"WebSocket","[Write] Shared Connections Don't Support Stream Sizes over 3.6MB",sc)
        Quit $$$Error($$$GeneralError, "[Write] Shared Connections Don't Support Stream Sizes over 3.6MB")
	} else {
        SET sc = $$$OK
        // Write header
		Write head
        // Rewind Stream
        Do data.Rewind()
        // Loop through stream and write 64kb chunks
        while (data.AtEnd = 0) {
            // Set Buffer to 64KB
            Set BUFSZ = 65536
            // Read Buffer
            SET BUFBLOCK = data.Read(.BUFSZ)
            // Convert UTF8 if we aren't using Binary Web Sockets
            If i%BinaryData '= 1 {
                Try {
                    Set BUFBLOCK=$zconvert(BUFBLOCK,"O","UTF8")
                } Catch exp {
                    $$$SysLog(2, "WebSocket", "[Write] Exception: "_exp.DisplayString(), data)
                    Set BUFBLOCK=BUFBLOCK
                }
            }
            // Use DEVICE WRITE method
            Write BUFBLOCK
            // Quit when done with stream
            BREAK:(BUFSZ < 65536)
        }
        // Send a flush command mnow
        Write *-3
        Quit sc
	}
	Quit sc
WriteError	
	#; No interrupts during cleanup or error processing
	Do event^%SYS.cspServer2("WebSocket Write Error: "_$ZError)
	$$$SetExternalInterrupts(0)
	Set $ZTrap="WriteHalt"
	Hang 5
	Close 0
WriteHalt 
	Halt
    
}
Nicholai Mitchko · Aug 13, 2022 go to post

Since the storage of passwords is only in salted & hashed format, you'll need to ask for the previous password, check that it logs the user in and then do the checking on the new password. That'll be your only time to view the previous password in plaintext.

Nicholai Mitchko · Aug 10, 2022 go to post

Have you tried wrapping your message call (Ens.Director and OnProcessInput) in a class on the target machine.

/// -------- This is on the source machine
/// Source (LIS) Method

ClassMethod ConnectAndCall(host, port, namespace, user, pwd) As %Status

{
    // Our object is defined somewhere
    #dim TRAN As %RegisteredObject = $$$NULLOREF
    Set sc = $$$OK
    // Connect to our HC machine
    set connection = ##class(%Net.DB.DataSource).CreateConnection(host, port, namespace, user, pwd)
    if 'connection.IsConnected set ERRTXT="NotConnected" quit $$$Error($$$GeneralError, "Couldn't connect")
    set irisC=connection.CreateIris()

 

    // Instead of multiple calling with response objects wrap everything on the client
    set response = irisC.ClassMethodValue("Helpers.Ens", "SendMessage", "ServiceName", TRAN)
    do connection.Disconnect()

 

    Return sc
}

 

/// ---------- This is on the target machine
/// healthconnect method (target machine)
/// Classname: "Helpers.Ens.SendMessage()"
ClassMethod SendMessage(servicename, message) As %RegisteredObject
{
    // Find running job of servicename
    Set sc = ##class(Ens.Director).CreateBusinessService(servicename, .serviceHandle)
    // Call that service
    Set sc = serviceHandle.OnProcessInput(message, .response)
    return response
}
Nicholai Mitchko · May 6, 2022 go to post

You could use a linked database (remote db projected as local tables) and then write an insert query to that linked table before it gets sent to a production.

Nicholai Mitchko · May 6, 2022 go to post

Your driver is not accessible in the container. To make it accessible, you'll need to either copy it into the container filesystem using a dockerfile like so,

COPY /Users/joost/mysql-connector-java-8.0.29.jar /

OR mount a volume to the container when you start it with the driver in the host path

Nicholai Mitchko · May 4, 2022 go to post

The method you want to change the language (locale or NLS) is Config.NLS.Locales -- Install()

You can see the available locales at this portal page: [System Administration] > [Configuration] > [National Language Settings] > Locale Definitions

So to put that into a manifest:

    <Namespace Name="%SYS" Create="no">
        <Log Text="Changing the Locale" Level="0"/>
        <Invoke Class="Config.NLS.Locales" Method = "Install" CheckStatus="true">
            <Arg Value="danw"/> 
            <!-- danw is the Danish Locale, Replace with Desired  -->
        </Invoke>
    </Namespace>
Nicholai Mitchko · Apr 25, 2022 go to post

If you're looking for how to run irispython --

In installation-dir/bin there is a binary irispython that is part of the installation Link

$ /usr/irissys/bin/irispython
Python 3.8.10 (default, Sep 28 2021, 16:10:42) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import iris
>>> iris.utils._OriginalNamespace()
'USER'

And to set the namespace:

import os
os.environ['IRISNAMESPACE'] = 'MyNamespace'
# must come after you set namespace or user
import iris

And to set the user credentials

import os
from getpass import getpass
os.environ['IRISUSERNAME'] = input('username> ')
os.environ['IRISPASSWORD'] = getpass('password >>>') 
# must come after you set namespace or user
import iris
Nicholai Mitchko · Apr 18, 2022 go to post

Sure, I will review the code and then add. I want to see if this could be accomplished with a ZPM package perhaps!

zpm "install code-server" would be very cool

Nicholai Mitchko · Jan 29, 2021 go to post

I am not sure! This will secure any traffic hosted by the instance itself on the port added to the config (10443 in the example). It also does not change the way links are generated. If the portal webpage uses relative links, then it could secure those requests, but they ultimately don't connect through the instance so really security is out of our hands there.

This method simply opens an additional port on the included Apache server secured by the self-signed certificate. The non-secure ports will still work so this isn't a viable production strategy.

Nicholai Mitchko · Aug 12, 2020 go to post

Hi Dan, Can you please update the following to make this tutorial up-to-date?

  • The endpoints in the Google1N and Google2n depend on your server and ssl port. Maybe make a note there that .../csp/google/Web.OAUTH2.... would be /csp/user/Web or /csp/namespaceABC/Web.Oauth2 depending on where you implement the code.
  • The issuing endpoints for the tutorial have changed to the following: https://accounts.google.com/.well-known/openid-configuration

Nicholai Mitchko · Sep 30, 2019 go to post

Glad to hear!

Depending on the length of the incoming request, you may hit the length of the Stream Read Buffer. See my updated code for larger messages as you may need to loop your read() statement,

cheers :)

Nicholai Mitchko · Sep 30, 2019 go to post

Hi Craig,

ImportFromString returns an HL7 Message, it does not populate an existing instance of EnsLib.HL7.Message.

Moreover, make sure that you are reading the entire message with %request.Content.Read, you'll probably want to wrap that in a loop.

Here is the code I get to work on my instance (2017.1)
 

SET tContent = %request.Content.Read()
WHILE '%request.Content.AtEnd {
SET tContent = tContent + %request.Content.Read()
}
SET tReq = {}.%FromJSON(tContent)
SET tInput = tReq.Message
// Remove your %New() statement and set tMsg like this
SET tMsg = ##class(EnsLib.HL7.Message).ImportFromString(tInput)
// Then you can either set the doctype manually or use PokeDocType()
// ...