Pesquisar

記事
· 2025年9月1日 9m read

Data Transformation Testing

InterSystems IRIS interoperability production development involves using or writing various types of components. They include services (which handle incoming data), processes (which deal with the data flow and logic), and operations (which manage outgoing data or requests). Messages flowing through those components constantly require being adapted to consuming applications. Therefore,Data transformations are by far the most common component in interoperability productions.

In the early stages of data transformation development, the test tool from the Management Portal becomes quite handy. It complements programmatic tests (unit tests), which can be implemented during a later stage to prevent regressions and ensure the transformations continue to function after changes are applied to the environment.

The first part of this article explains how to use the test tool user interface available in Ensemble 2007 and later versions. We can invoke it directly from Visual Studio Code IDE with any class that extends Ens.DataTransform.

Please note that a new user interface is now available for DTL Editor and the test tool. In the first part of this article, we will examine the current user interface (available in Ensemble 2007 and later versions). In the second part, on the other hand, we will explore the new interface (available in IRIS 2025.1 and later versions).

The third and final part of this article will detail unit testing data transformations. It will also outline some guidelines and good practices for data transformation coding.

Part 1: Using the current Test Tool User Interface

The test tool can be accessed from either Studio or the Management Portal. It is also possible to configure a server action to invoke it directly from VS Code.

To access the test tool from the Management Portal, navigate to Interoperability > Build > Data Transformation and open a transformation using the 'Open' button. The dialog will allow you to select only DTL transformations. However, the test tool supports any class that extends Ens.DataTransform (more on this later).

   

The URL for the tool, when invoked from the Management Portal, should look like the following:

http://localhost:58603/csp/healthshare/irisapp/EnsPortal.Dialog.TestTransform.zen?$ZEN_POPUP=1&$ZEN_SOFTMODAL=1&TRANSFORM=test.SampleTransformDTL

Let’s have a closer look at the URL individual components:

  • Server base URL: it is the IRIS CSP gateway base URL
  • Test component path: it is the path of the (zen) test tool component
  • The remaining part of the URL has component parameter values:
    • $ZEN_POPUP: true (1) - it means that this is a pop window
    • $ZEN_SOFTMODAL: true (1) - it means that this window is modal
    • TRANSFORM: it is a data transform class name

Invoking the tool from VS Code

 

Using the test component's URL, you can invoke a tool from VS Code with the help of a server action. To do that, simply add an extra link to the "links" key of objectscript.conn in settings.json.

For IRIS for Health:

"objectscript.conn": {
       "links": {
         "Data Transform Test": "${serverUrl}/csp/healthshare/${namespace}/EnsPortal.Dialog.TestTransform.zen?$ZEN_POPUP=1&$ZEN_SOFTMODAL=0&TRANSFORM=${classname}"
       }
     }
 
For IRIS Data Platform:
"objectscript.conn": {
       "links": {
         "Data Transform Test": "${serverUrl}/csp/${namespace}/EnsPortal.Dialog.TestTransform.zen?$ZEN_POPUP=1&$ZEN_SOFTMODAL=0&TRANSFORM=${classname}"
       }
     }

 

The link uses the following variables exposed by the ObjectScript extension:

  • ${serverUrl}: The connected server base URL.
  • ${namespace}: The connected namespace.
  • ${classname}: The currently open class name.

 

For more details on configuring server actions, see Configure and Perform Server Actions within VS Code | Use Visual Studio Code as a Development Environment for InterSystems Applications | InterSystems Components and Tools.

While the dialog in the portal allows you to select only DTL transformations, the test tool supports any class that extends Ens.DataTransform.

For DTL transformations, it gets the source and target classes from the DTL <transform> element's attributes. For non-DTL transformations (classes that extend  Ens.DataTransform), the tool uses the Transform() method signature.

When the source and/or target class is a virtual document class (a class that extends Ens.VDoc.Interface), a document type (DocType) is required. For DTL data transformations, the tool gets the <transform> element's attributes, SourceDocType, and TargetDocType. For non-DTL transformations, an implementation of GetSourceDocType() and/or GetTargetDocType() is a must.

Example

Below you can find a complete example of a non-DTL data transformation (extending Ens.DataTransform directly) that can be used with the test tool. The transformation clones a source HL7 v2.3 SIU message and sets the target message SCH:7.1 field component value.

Class ut.ks.lib.interop.SampleTranform Extends Ens.DataTransform
{ 
ClassMethod Transform(source As EnsLib.HL7.Message, Output target As EnsLib.HL7.Message, aux As %String) As %Status
{
    #Dim sc as %Status
    #Dim ex as %Exception.AbstractException
    s sc = $$$OK
    try {
        s target = source.%ConstructClone(1)        
        s target.DocType = ..GetTargetDocType()
        d target.SetValueAt("hello","SCH:7.1")   
    } catch (ex) {
      s sc = ex.AsStatus()
    }
    return sc
}
 
ClassMethod GetSourceDocType() As %String
{
  return "2.3:SIU_S12"
}
 
ClassMethod GetTargetDocType() As %String
{
  return "2.3:SIU_S12"
}
 
}

 Take a look at a little screencast of this code in action below:

 

Part 2: Using the Test Tool with the new user interface

Starting with InterSystems IRIS version 2025.1, the DTL editor and test tool got a new, alternative user interface accessible from the Management Portal or VSCode IDE.

To access the new UI from the Management Portal, go to Interoperability > Build > Data Transformation and open a transformation using the ‘open’ button. Remember that the dialog will let you select only DTL transformations. To switch to the new user interface, click the “new UI” button. 
The new user interface can also be accessed as an editor directly in VS Code.

Unlike the previous version, the new test tool supports only DTL data transformations. To use it with a class that extends Ens.DataTransform, you must wrap the call to the Transform() class method in a code action. Review the following example:

Class ut.ks.lib.interop.SampleTransformDTL Extends Ens.DataTransformDTL [ DependsOn = EnsLib.HL7.Message ]
{
 
Parameter IGNOREMISSINGSOURCE = 0;
Parameter REPORTERRORS = 0;
Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;
Parameter GENERATEEMPTYSEGMENTS = 0;
XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='EnsLib.HL7.Message' targetClass='EnsLib.HL7.Message' sourceDocType='2.3:SIU_S12' targetDocType='2.3:SIU_S12' create='new' language='objectscript' >
<code>
<![CDATA[
    return ##class(ut.ks.lib.interop.SampleTranform).Transform(source,.target,.aux)
  ]]></code>
</transform>
}
}

 

Part 3: Programmatic testing

Using the %UnitTest framework, it is easy to write repeatable and robust programmatic tests for data transformations. See @Yuri Marx excellent article on the topic and the official documentation for more.

It is generally a good practice for transformations to depend as little as possible on runtime context data other than the source and auxiliary objects.

Generally speaking, operating anything other than the source, target, auxiliary objects, or the current namespace content should be avoided. Below you can check some examples of what should be bypassed:

  • Depending on interoperability production (e.g., sending a request to a business operation).
  • Having a stateful Transform() class method.
  • Performing a file or external database input/output.

If additional runtime data is needed, the transformation class should provide default values to the maximum extent possible. To achieve that, add such members as class parameters or XData blocks to the transformation class.

Parameters can hold any string value (even multi-line), and when declared as a configuration value, can be changed in a given namespace after class compilation (a nice, yet underrated configuration mechanism).

XData blocks are more suitable for small XML documents, JSON objects or arrays, or text-structured data. Remember to takegeneral system limits into account, though.

Larger data may be made available to the transformation using the following:

The Auxiliary parameter “aux” is passed by the caller of the Transform() class method. It can be a datatype value or an OREF. When the transformation is used in the ‘send’ action of a message router rule, the router passes itself as an aux parameter, and its properties (e.g., RuleActionUserData) become available to the transformation.

If the transformation class cannot provide the defaults, the test should deliver them and initialize any required state before running the Transform() method. If the transformation is, for instance, using a custom HL7 schema, it should be loaded before the test run. The test should, as an example, do the following:

A unit test for a transformation begins by initializing context data (e.g., import lookup tables). Then, for each test source object, it calls the Transform() method and compares the resulting target object coming from the transformation to the expected outcome.

Anticipated results can be stored as files loaded by the test at runtime or XData blocks. With many inputs to test (e.g., when testing multiple complete HL7 v2.x ADT scenarios), I find files to be more practical.

Example

Test Class

This test uses the ks-iris-lib module from Open Exchange to import HL7 messages from XData blocks and compare the target message from the transformation to the expected result:

Class dc.SampleTransformTest Extends ks.lib.test.TestCase
{
 
Method TestTransform()
{
    #Dim sc as %Status
    #Dim ex as %Exception.AbstractException
    #Dim source,target,expected As EnsLib.HL7.Message
   
    s sc = $$$OK
    try {
        set source = ##class(ks.lib.hl7.Utils).ImportFromXData($classname(),"SourceHL7","2.3",.sc)
        $$$TOE(sc,sc)
        set expected = ##class(ks.lib.hl7.Utils).ImportFromXData($classname(),"ExpectedHL7","2.3",.sc)
        $$$TOE(sc,sc)
        $$$TOE(sc,##class(NullTranform).Transform(source,.target,$this))
        do $$$AssertTrue(..CompareHL7Messages(target,expected))
    } catch (ex) {
      s sc = ex.AsStatus()
    }
    do $$$AssertStatusOK(sc)
}
 
XData SourceHL7 [ MimeType = application/hl7 ]
{
MSH|^~\&|SPOCARD|EWHIN|JONES|EWHIN|199401040800||SIU^S13|021244SPOCARD|P|2.3|||AL|ER||
SCH|1994047^SCH001|1994567^SCH100|||||047^Referral|NORMAL|30|min|^^^199401091300^199401091330^^^^|0045^Jones^Harold^S^^^MD|555-4685|||087^Jensen^Helen^M^^^MD|555-9255||||BOOKED
NTE||The patient is going to be on vacation so cannot make previous appointmentscheduled on January 6.
PID||4875439|484848||Peterson^Joseph^^Jerome^SR|Brown|19401121|M|Jayjay||N 1234 Newport Highway^Mead^WA^99021||555-4685|||M|||999-99-4413|||||||||||
RGS|001|
AIP|001|032^JENSEN^HELEN|002^CARDIOLOGIST|||||||NO|BOOKED
}
 
XData ExpectedHL7 [ MimeType = application/hl7 ]
{
MSH|^~\&|SPOCARD|EWHIN|JONES|EWHIN|199401040800||SIU^S13|021244SPOCARD|P|2.3|||AL|ER||
SCH|1994047^SCH001|1994567^SCH100|||||047^Referral|NORMAL|30|min|^^^199401091300^199401091330^^^^|0045^Jones^Harold^S^^^MD|555-4685|||087^Jensen^Helen^M^^^MD|555-9255||||BOOKED
NTE||The patient is going to be on vacation so cannot make previous appointmentscheduled on January 6.
PID||4875439|484848||Peterson^Joseph^^Jerome^SR|Brown|19401121|M|Jayjay||N 1234 Newport Highway^Mead^WA^99021||555-4685|||M|||999-99-4413|||||||||||
RGS|001|
AIP|001|032^JENSEN^HELEN|002^CARDIOLOGIST|||||||NO|BOOKED
}
 
}
 

Transform Class

Class dc.NullTransform Extends Ens.DataTransform
{
 
ClassMethod Transform(source As EnsLib.HL7.Message, Output target As EnsLib.HL7.Message, aux As %String) As %Status
{
    #Dim sc as %Status
    #Dim ex as %Exception.AbstractException
    s sc = $$$OK
    try {
        s target = source.%ConstructClone(1)        
        s target.DocType = ..GetTargetDocType()
    } catch (ex) {
      s sc = ex.AsStatus()
    }
    return sc
}
 
ClassMethod GetSourceDocType() As %String
{
  return "2.3:SIU_S12"
}
 
ClassMethod GetTargetDocType() As %String
{
  return "2.3:SIU_S12"
}
 
}
ディスカッション (0)3
続けるにはログインするか新規登録を行ってください
ダイジェスト
· 2025年9月1日

Publications des développeurs d'InterSystems, semaine Août 25 - 31, 2025, Résumé

ダイジェスト
· 2025年9月1日

Résumé de la communauté des développeurs d'InterSystems, Août 2025

Bonjour, voici la newsletter de la communauté des développeurs de Août 2025.
Statistiques générales
✓ Nouvelles publications 22 publiées le Août :
 12 nouveaux articles
 10 nouvelles annonces
✓ Nouveaux membres 3 ayant rejoint le Août
✓ Publications 1,244 publiées depuis le début
✓ Membres 185 ayant rejoint depuis le début
Meilleures publications
Les meilleurs auteurs du mois
Articles
#InterSystems IRIS
Configuration d'OpenTelemetry dans IRIS
Par Sylvain Guilbaud
Comment créer votre propre table de recherche pour la messagerie HL7
Par Lorenzo Scalese
Utilisation de LIKE avec des variables et des modèles dans SQL
Par Iryna Mykhailova
Un guide pour débutants sur la création de tables SQL et leur visualisation en tant que classes
Par Iryna Mykhailova
Hébergement d'une application Flask de l'API REST sur InterSystems IRIS à l'aide de l'interface WSGI
Par Liam Evans
Pilote pour connecter IRIS à Metabase
Par Guillaume Rongier
Pourquoi devons-nous établir des règles ou des conventions de codage ?
Par Lorenzo Scalese
Traçage des applications InterSystems IRIS à l'aide de Jaeger
Par Iryna Mykhailova
Comment contrôler l'ordre des colonnes affichées lors de l'accès à partir d'un outil ODBC / JDBC
Par Sylvain Guilbaud
#Autre
#InterSystems IRIS for Health
#Communauté des développeurs officielle
Annonces
#Communauté des développeurs officielle
#IRIS contest
#InterSystems IntelliCare
#Global Masters
#InterSystems IRIS for Health
#InterSystems officiel
#HealthShare
Août, 2025Month at a GlanceInterSystems Developer Community
記事
· 2025年9月1日 3m read

Programación práctica en ObjectScript: de JSON a Globals a SQL

Al comenzar con InterSystems IRIS o Caché, los desarrolladores a menudo se encuentran con tres conceptos fundamentales: Dynamic Objects, Globals y Relational Table. Cada uno tiene su papel en la construcción de soluciones escalables y mantenibles. En este artículo recorreremos ejemplos prácticos de código, destacaremos buenas prácticas y mostraremos cómo estos conceptos se relacionan entre sí.

1. Trabajando con Dynamic Objects:

Los objetos dinámicos (%DynamicObject y %DynamicArray) permiten a los desarrolladores manipular estructuras similares a JSON directamente en ObjectScript. Son especialmente útiles para aplicaciones modernas que necesitan analizar, transformar o generar JSON.

Ejemplo: Crear y manipular Dynamic Objects

    // Create a Dynamic object
    Set obj - {}

    // Add properties
    Set obj.name = "Vachan"
    Set obj.age = 25
    // Nested objects
    Set obj.address = {"city":"Bengaluru", "zip":"560000"}
    
    // Add an Array
    Set obj.skills = ["Objectscript", "SQL"]
    
    // Convert to JSON string
    Set json = obj.%ToJSON()
    Write json,!
    
    // Parse JSON string back to an object
    Set parser = {}.%FromJSON(json)
    Write parser.name

Mejores prácticas

  • Validad siempre la entrada JSON con %FromJSON() para detectar errores.
  • Usad obj.%Get("property") cuando no estéis seguros de que una propiedad exista.
  • Preferid %DynamicArray para estructuras de tipo lista.

2. Usando Globals de manera efectiva:

Los Globals son arrays dispersos jerárquicos almacenados directamente en el motor de la base de datos IRIS. Son extremadamente rápidos y pueden almacenar prácticamente cualquier estructura.

Ejemplo: Almacenando datos en Globals

// Store student data in a global
SET ^Student(1,"Name") = "Alice"
SET ^Student(1,"Age") = 29
SET ^Student(2,"Name") = "Bob"
SET ^Student(2,"Age") = 34
// Retrieve data
WRITE ^Student(1,"Name")  // outputs: Alice
// Iterate over all students
SET id=""
FOR  SET id=$ORDER(^Student(id)) QUIT:id=""  {
    WRITE "Student ",id,": ",^Student(id,"Name")," (Age ",^Student(id,"Age"),")",!
}

Mejores prácticas:

  • Definid una estructura clara para el global antes de codificar (evitad claves ad-hoc).
  • Usad globals para almacenamiento de alto rendimiento cuando no se necesite la sobrecarga de SQL.
  • Para los datos de la aplicación, preferid clases persistentes con globals gestionados internamente.

3. Creación de tablas relacionales SQL:

En IRIS, las tablas relacionales se pueden crear tanto usando SQL DDL como clases persistentes.

Ejemplo: Crear una tabla SQL mediante DDL

CREATE TABLE Employee (
    ID SERIAL PRIMARY KEY,
    Name VARCHAR(50),
    Age INT,
    Department VARCHAR(50)
);

Ejemplo: Crear la misma tabla como Clase Persistente

Class Company.Employee Extends (%Persistent) {
    Property Name As %String(MAXLEN=50);
    Property Age As %Integer;
    Property Department As %String(MAXLEN=50);
}

Una vez compilada, esta clase crea automáticamente un global subyacente y una tabla SQL. Ahora podéis usar tanto ObjectScript como SQL:

// Create and save an employee
SET emp = ##class(Company.Employee).%New()
SET emp.Name = "Charlie"
SET emp.Age = 40
SET emp.Department = "IT"
DO emp.%Save()

// Query employees with SQL
&sql(SELECT Name, Age FROM Company.Employee)
WHILE (SQLCODE=0) {
    WRITE "Employee: ",Name,", Age: ",Age,!
    FETCH NEXT
}

Mejores Prácticas:

  • Preferid clases persistentes para aplicaciones mantenibles.
  • Usad SQL DDL para definiciones rápidas de tablas o integración con sistemas externos.
  • Definid siempre índices para las propiedades que se consultan con frecuencia.

RESUMEN:

Ya sea que estéis analizando cargas JSON, gestionando datos de acceso rápido o diseñando tablas relacionales, entender cuándo usar objetos dinámicos, globals o clases persistentes es clave para convertiros en desarrolladores efectivos de ObjectScript.

ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
記事
· 2025年9月1日 7m read

Crea un agente de IA para atención al cliente con smolagents + InterSystems IRIS (SQL, RAG e interoperabilidad)

Las preguntas de atención al cliente pueden abarcar datos estructurados (pedidos, productos 🗃️), conocimiento no estructurado (docs/FAQs 📚) y otros sistemas integrados (actualizaciones de envío 🚚). En este post vamos a construir un agente de IA compacto que cubre los tres—usando:

  • 🧠 Python + smolagents para orquestar el “cerebro” del agente
  • 🧰 InterSystems IRIS para SQL, Búsqueda Semántica (RAG) e Interoperabilidad (una API de seguimiento de envío simulada)

1 Comment
ディスカッション (1)2
続けるにはログインするか新規登録を行ってください