#Mejores prácticas

0 Seguidores · 90 Publicaciones

Sugerencias de prácticas recomendadas sobre cómo desarrollar, probar, implementar y administrar más eficientemente las soluciones en InterSystems Data Platform. 

Artículo Ricardo Paiva · sep 12, 2022 5m read

freepik- freepik.com En primer lugar, ¿qué es la anonimización de datos?

Según la Wikipedia:

La anonimización es un tipo de sanitización de información cuya intención es la protección de la privacidad. Es el proceso de eliminar información personal de los conjuntos de datos, de modo que las personas que son descritas por los datos permanecen en el anonimato.

En otras palabras, la anonimización de datos es un proceso que conserva los datos pero mantiene la fuente anónima. Según la técnica de anonimización que se adopte, los datos son editados, ocultados o sustituidos.

Y ese es el propósito de iris-Disguise, ofrecer un conjunto de herramientas de anonimización.

Lo puedes usar de dos formas diferentes, por método de ejecución o especificar tu estrategia de anonimización dentro de la definición de la clase persistente en sí misma.

La actual versión de iris-Disguise ofrece 6 estrategias para anonimizar datos:

  • Destruction (Destrucción)
  • Scramble (Codificación)
  • Shuffling (Reorganización)
  • Partial masking (Ocultación parcial)
  • Randomization (Distribución aleatoria)
  • Faking (Falsificación)

Vamos a explicar cada estrategia. Mostraré un método de ejecución con un ejemplo como he mecionado, y también mostraré cómo aplicarlo dentro de la definición de la clase persistente. Para usar iris-Disguise de esta forma, necesitaréis "llevar unas gafas de disfraz". En la clase persistente, se puede extender la clase dc.Disguise.Glasses y cambiar cualquier propiedad con los tipos de datos con la estrategia que prefieras. Tras ello, en cualquier momento, simplemente llama al método DisguiseProcess en la clase. Todos los valores serán sustituidos usando la estrategia del tipo de datos.

Así que... abrochaos el cinturón que empezamos!

Destruction (Destrucción)

Esta estrategia reemplazará una columna entera con una palabra ('CONFIDENTIAL' (CONFIDENCIAL) por defecto).

Do ##class(dc.Disguise.Strategy).Destruction("classname", "propertyname", "Word to replace")

El tercer parámetro es opcional. Si no se aporta, se usará la palabra 'CONFIDENTIAL' (CONFIDENCIAL).

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As dc.Disguise.DataTypes.String(FieldStrategy = "DESTRUCTION");
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

1

Scramble (Codificación)

Esta estrategia codificará todos los caracteres de una propiedad.

Do ##class(dc.Disguise.Strategy).Scramble("classname", "propertyname")

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As dc.Disguise.DataTypes.String(FieldStrategy = "SCRAMBLE");
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

scramble

Shuffling (Reorganización)

Shuffling reorganizará todos los valores de una propiedad dada. No es una estrategia de ocultación porque trabaja "verticalmente". Esta estrategia es útil para relaciones, porque se mantendrá la integridad referencial. Hasta esta versión, este método solo funciona en relaciones de uno a muchos.

Do ##class(dc.Disguise.Strategy).Shuffling("classname", "propertyname")

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As %String;
Property Weapon As dc.Disguise.DataTypes.String(FieldStrategy = "SHUFFLING");
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

shuffling

Partial Masking (Ocultación parcial)

Esta estrategia ocultará parte de los datos. Por ejemplo, el número de una tarjeta de crédito puede ser sustituido por 456X XXXX XXXX X783

Do ##class(dc.Disguise.Strategy).PartialMasking("classname", "propertyname", prefixLength, suffixLength, "mask")

PrefixLength, suffixLength y mask son opcionales. Si no se aporta, se usarán los valores por defecto.

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As %String;
Property SSN As dc.Disguise.DataTypes.PartialMaskString(prefixLength = 2, suffixLength = 2);
Property Weapon As %String;
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

partialmsk

Randomization (Distribución aleatoria)

Esta estrategia generará sencillamente datos aleatorios. Hay tres tipos de distribución aleatoria: integer, numeric y date.

Do ##class(dc.Disguise.Strategy).Randomization("classname", "propertyname", "type", from, to)

type: "integer", "numeric" o "date". "integer" es el predeterminado.

"from" y "to" son opcionales. Es para definir el rango de distribución aleatoria. Para el tipo "integer" el rango por defecto es de 1 a 100. Para el tipo "numeric" el rango por defecto es de 1.00 a 100.00.

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As %String;
Property Age As dc.Disguise.DataTypes.RandomInteger(MINVAL = 10, MAXVAL = 25);
Property SSN As %String;
Property Weapon As %String;
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

rand

Faking (Falsificación)

La idea de la falsificación es reemplazar datos con valores aleatorios pero plausibles. iris-Disguise proporciona una pequeño conjunto de métodos para generar datos falsos.

Do ##class(dc.Disguise.Strategy).Fake("classname", "propertyname", "type")

type: "firstname", "lastname", "fullname", "company", "country", "city" y "email"

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As dc.Disguise.DataTypes.FakeString(FieldStrategy = "FIRSTNAME");
Property Age As %Integer;
Property SSN As %String;
Property Weapon As %String;
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

fake

### ¿Qué os parece?

Me gustaría conocer vuestra opinión y leer vuestros comentarios - qué os parece, si se ajusta a vuestras necesidades y qué funcionalidades echais de menos.

Y quería agradecer de forma especial a @Henrique Dias, @Oliver Wilms, @Robert Cemper, @Yuri Marx y @Evgeny Shvarov sus comentarios, revisiones, sugerencias y valiosos aportes, que me han inspirado tanto y ayudado a crear y mejorar iris-Disguise.

1
1 212
Artículo Nancy Martínez · mayo 30, 2022 1m read

Estoy seguro de que os habéis encontrado esta situación:

  • Hay un bug en un sistema que no puedes reproducir por ti mismo localmente
  • Necesitas ejecutar unas pocas líneas en la instancia afectada
  • Tienes acceso completo al Portal de Gestión
  • Pero no hay terminal, ni consola, ni acceso con Studio, Atelier o VSCode
  • ¿Cómo ejecutar tus pocas líneas para probar???
1
0 203
Artículo Jose-Tomas Salvador · oct 6, 2022 6m read

Python se ha convertido en el lenguaje de programación más utilizado del mundo (fuente: https://www.tiobe.com/tiobe-index/) y SQL sigue siendo el líder como lenguaje para las bases de datos. ¿No sería genial que Python y SQL trabajaran juntos para ofrecer nuevas funcionalidades que SQL por sí mismo no puede? Después de todo, Python tiene más de 380.000 librerías publicadas (fuente: https://pypi.org/) con funciones muy interesantes para ampliar las consultas SQL dentro de Python.

En este artículo detallo cómo crear nuevos Procedimientos Almacenados de SQL en la base de datos de InterSystems IRIS usando Embedded Python.

Librerías de Python utilizadas como muestras

En este artículo utilizaré dos librerías muy útiles para cualquiera que trabaje con SQL en IRIS: Geopy y Chronyk.

Geopy es una librería utilizada para aplicar la geocodificación (asignación de coordenadas geográficas a direcciones) a los datos de direcciones. Con ella es posible, a partir del nombre de la calle, obtener el código postal y la dirección completa, en el formato de la oficina de correos. Es muy útil, ya que muchos registros tienen una dirección.

Chronyk se utiliza para procesar fechas y horas mediante el lenguaje humano. Esto es muy útil, porque internamente, tanto para IRIS como para Python, una fecha es un número que representa la cantidad de tiempo que ha transcurrido desde una fecha inicial. Para los humanos, una fecha es el 20 de julio, o ayer, o mañana, o hace dos horas. Chronyk acepta recibir la fecha así y luego la convierte al formato de fecha universal.

Soporte de Python en InterSystems IRIS

Desde la versión 2021.2 es posible utilizar Python para crear métodos de clase, procedimientos almacenados, producciones de interoperabilidad y llamadas nativas entre Python e IRIS (ObjectScript) de forma bidireccional. No conozco ninguna otra plataforma de datos que trabaje tan estrechamente con Python. El requisito para que esto funcione es que Python se instale en la misma máquina física o virtual o contenedor que IRIS. Consulta más detalles en: https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_epython.

Para instalar Python ejecuta:

# install libraries required for python and pip
RUNapt-get-yupdate\
    &&apt-get-yinstallapt-utils\
    &&apt-getinstall-ybuild-essentialunzippkg-configwget\
    &&apt-getinstall-ypython3-pip  

Soporte de librerías de Python en InterSystems IRIS

Para que InterSystems IRIS pueda utilizar una librería de Python es obligatorio que esté instalada dentro de <installdir>/mgr/python. installdir es la carpeta en la que está instalado IRIS. Para instalar nuevos paquetes ejecuta:

# use pip3 (the python zpm) to install geopy and chronyk packages
RUNpip3install--upgradepipsetuptoolswheel
RUNpip3install--target/usr/irissys/mgr/pythongeopychronyk

Pip3 es el administrador e instalador de paquetes más popular de Python, Pip.

Cómo crear Procedimientos Almacenados en lenguaje de Python

Una de las posibilidades de utilizar Python en InterSystems IRIS es crear Procedimientos Almacenados usando Python.

Hay dos posibilidades:

  1. Crear un Procedimiento Almacenado en Python utilizando la sentencia SQL de crear función o procedimiento
  2. Crear un Método de clase dentro de la clase ObjectScript con las etiquetas sqlProc y language=Python.

Cómo crear un Procedimiento Almacenado en Python utilizando la sentencia SQL de Crear Procedimiento

Según la documentación de InterSystems, también se puede escribir una función SQL o un Procedimiento Almacenado utilizando Python Embebido si se especifica el argumento LANGUAGE PYTHON en la sentencia CREATE, como se muestra a continuación (fuente: https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?KEY=AEPYTHON#AEPYTHON_runpython_sql):

CREATE FUNCTION tzconvert(dt TIMESTAMP, tzfrom VARCHAR, tzto VARCHAR)
    RETURNS TIMESTAMP
    LANGUAGE PYTHON
{
    from datetime import datetime
    from dateutil import parser, tz
    d = parser.parse(dt)
    if (tzfrom is not None):
        tzf = tz.gettz(tzfrom)
        d = d.replace(tzinfo = tzf)
    return d.astimezone(tz.gettz(tzto)).strftime("%Y-%m-%d %H:%M:%S")
}

Cuando se ejecuta esta nueva función SQL:

SELECT tzconvert(now(), 'US/Eastern', 'UTC')

La función devuelve algo como:

2022-07-20 15:10:05

Cómo crear el Método de clase dentro de la clase ObjectScript con las etiquetas sqlProc y language=Python

Confieso que este enfoque es mi favorito: crear un método de clase con las etiquetas sqlProc y language=Python.

En mi opinión es más fácil de mantener, está mejor documentado y con una mejor administración de las versiones del código fuente. Para este enfoque he publicado una aplicación de muestra: https://openexchange.intersystems.com/package/Python-IRIS-SQL-Procedures-Sample. La utilizaré para mostrar este segundo enfoque con detalle.

Instalación de la aplicación de muestra

Para instalar la aplicación de muestra, sigue estos pasos:

  1. Clonar/git pull el repositorio en cualquier directorio local: $ git clone https://github.com/yurimarx/iris-sql-python-sample.git
  2. Abrir un terminal de Docker en este directorio y ejecutar: $ docker-compose build
  3. Ejecutar el contenedor de IRIS: $ docker-compose up -d

Otra posibilidad para instalar es utilizar el ZPM:

zpm "install iris-sql-python-sample"

Ejemplos de Procedimientos Almacenamiento usando Python

El primer ejemplo es un Procedimiento Almacenado para administrar la geocodificación de direcciones, consulta el código fuente:

ClassMethodGetFullAddress(StreetAs%String,CityAs%String,StateAs%String)
As%String[Language=python,SqlName=GetFullAddress,SqlProc]
{
    importgeopy.geocoders
    fromgeopy.geocodersimportNominatim
    geopy.geocoders.options.default_timeout =7
    geolocator =Nominatim(user_agent="intersystems_iris")
    location =geolocator.geocode(Street +", " +City +", " +State,country_codes="US")
    returnlocation.address
}

Fíjate que se declaró un Método de clase (dentro de la clase dc.pythonsql.Company) con las etiquetas [ Language = python, SqlProc]. La etiqueta SqlName permite establecer un nombre para el nuevo Procedimiento Almacenado en sentencias SQL. Ve al Portal de administración, Sistema > SQL y ejecuta el siguiente código:

SELECT ID, City, Name, State, Street, Zip, dc_pythonsql.GetFullAddress(Street, City, State) As FullAddress 
FROM dc_pythonsql.Company

Verás estos resultados: Geopy

Ahora las direcciones incompletas regresan como direcciones "completas" (completas y calificadas).

Nota: Si no devuelve nada, ejecuta #class(dc.pythonsql.Company).CreateFiveCompanies(). Creará cinco empresas para utilizar en las pruebas.

Este paquete puede funcionar con los principales servicios de geocodificación abiertos y comerciales. En este ejemplo utilizamos el servicio abierto Nominatim, pero es posible utilizar Bing, Google, ArcGIS y otros. Consulta las posibilidades en https://geopy.readthedocs.io/en/stable/#module-geopy.geocoders.

El segundo ejemplo es un paquete de fecha y hora en formato humanizado, Chronyk. Permite enviar frases como "mañana", "ayer", "dentro de 4 horas", "4 de julio del 2022" y obtener el resultado en formato de fecha universal. Mira la creación del Procedimiento Almacenado:

ClassMethodGetHumanDate(SentenceAs%String)As%String[Language=python,SqlName=GetHumanDate,SqlProc]
{
    fromchronykimportChronyk
    t =Chronyk(Sentence)
    returnt.ctime()
}

Realiza la siguiente llamada en el Portal de administración > Sistema > SQL:

SELECT ID, City, Name, State, Street, Zip, dc_pythonsql.GetHumanDate('yesterday') As Datetime FROM dc_pythonsql.Company

Verás resultados como este: https://raw.githubusercontent.com/yurimarx/iris-sql-python-sample/main/screen2.png

Si quieres simplemente llamar al Procedimiento Almacenado, puedes usar esta sentencia SQL:

select dc_pythonsql.GetHumanDate('yesterday') as Datetime  

Esta librería tiene varias posibilidades de fechas y horas humanizadas, consulta https://github.com/KoffeinFlummi/Chronyk. Así que es fácil crear Procedimientos Almacenados en Python. ¡Disfrútalo!

1
1 764
Artículo Alberto Fuentes · mayo 20, 2022 3m read

Si estás desarrollando soluciones en IRIS y quieres utilizar Git, ¡es genial! Sólo tienes que utilizar VSCode con un repositorio git local y enviar los cambios al servidor, así de fácil.

Pero qué sucede si:

  • Colaboras con otros desarrolladores en un entorno de desarrollo remoto compartido y quieres evitar la edición simultánea del mismo archivo
  • Utilizas editores basados en el portal de administración para BPL, DTL, tablas dinámicas, cuadros de mando, etc. y quieres incluir esos elementos en el control de código de forma sencilla.
  • Aún utilizas Studio para realizar algunas cosas y/o ocasionalmente vuelves a usarlo desde VSCode. O tu equipo todavía no ha adoptado completamente VSCode y algunos miembros del equipo todavía quieren utilizar Studio
  • Trabajas en varios proyectos diferentes al mismo tiempo en el mismo namespace - digamos, en varios paquetes definidos mediante InterSystems Package Manager - y quieres trabajar con todos ellos desde una vista de edición de isfs (en vez de en varios proyectos distintos) con los cambios rastreados automáticamente en el repositorio de git apropiado

En ese caso no era tan sencillo... hasta finales del año pasado, cuando lanzamos Git para Entornos de Desarrollo Compartidos (Open Exchange / GitHub).

Puedes descargar esta extensión mediante InterSystems Package Manager:

zpm "install git-source-control"

Antes de esto, las opciones para controlar las fuentes con Git eran una antigua extensión de Git, principalmente para Windows, y sólo para entornos de desarrollo local y un proyecto más reciente de Open Exchange basado en él pero más sencillo. También está Port, que únicamente maneja archivos y es independiente del sistema de control de versiones.

¿Qué tiene git-source-control que no tienen estos paquetes?

  • Una integración sencilla con git, basada en un menú, que funciona en cualquier sistema operativo
  • Una interfaz de usuario de git para cubrir un conjunto de operaciones comunes de git, sin tener que conectarse con SSH al entorno remoto.
  • Control de concurrencia para múltiples usuarios que trabajan en el mismo entorno al mismo tiempo. Una vez que realizas cambios en una clase/rutina/etc., se vuelve tuya hasta que se descarten o se confirmen los cambios. (Sin embargo, tenemos formas de evitarlo cuando es necesario)
  • Integración con el administrador de paquetes: simplemente usa zpm mediante "load -dev /path/to/package" y si "/path/to/package/.git" existe, los cambios en los recursos de tu paquete se reflejarán automáticamente en el sitio correcto del sistema de archivos del servidor. La interfaz de usuario también funciona con esto, basada en la clase/etc. desde la que se ejecuta.

Todo esto funciona desde VSCode:

Spoiler

Y Studio:

 
Spoiler

Para que tengas el control y la visión de tu repositorio de git:

 
Spoiler

Esperamos que esto permita desarrollar con éxito las soluciones basadas en IRIS.

¡Agradecemos vuestros comentarios!

NOTA: Para ver la presentación del lanzamiento en el Global Summit de 2021, consultad este artículo: https://community.intersystems.com/post/video-git-gitlab-shared-development-environments

1
1 273
Artículo Ricardo Paiva · feb 24, 2023 11m read

En el primer artículo hablé sobre probar y depurar aplicaciones web de Caché con herramientas externas. La segunda parte tratará sobre las herramientas de Caché.

Estas son:

  • CSP Gateway y Webapp configuration
  • CSP Gateway logging
  • CSP Gateway tracing
  • ISCLOG
  • Custom logging
  • Session events
  • Output to device

CSP Gateway y Webapp configuration

En primer lugar, si estás depurando y, sobre todo, desarrollando una aplicación front-end, no necesitas caching. Es muy útil en un sistema de producción, pero no durante el desarrollo. Para desactivar el registro de una aplicación web, hay que ir a: SMP → Menu → Manage Web Applications → <Your Web App> y definir Serve Files Timeout con la configuración igual a 0. Luego hacer clic en "Save".

Después, hay que purgar el caché de la aplicación web. Para ello, ve a: SMP → System Administration → Configuration → CSP Gateway Management → System Status. Allí se encuentra la tabla "Cached Forms", la última fila es una línea Total, pulsa el botón borrar (con el punto) para borrar el caché de la aplicación web:

CSP Gateway logging

Ya que estamos hablando de CSP Gateway, este es compatible con el registro de las solicitudes de entrada (documentación). En la pestaña Default Parameters, especifica el nivel de registro deseado (por ejemplo, v9a) y guarda los cambios. v9a (consulta otras opciones en la documentación) registra todas las solicitudes HTTP a http.log en el directorio principal de Gateway. Este es un ejemplo de solicitud capturada:

GET /forms/form/info/Form.Test.Person HTTP/1.1
Host: localhost:57772
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:57772/formsui/index.html
Cookie: CSPSESSIONID-SP-57772-UP-forms-=001000000000yxiocLLb8bbc9SVXQJC5WMU831n2sENf4OGeGa; CSPWSERVERID=144zwBTP
Dnt: 1
Connection: keep-alive
Cache-Control: max-age=0
&lt;>

También hay opciones para registrar el rendimiento. Los resultados pueden escribirse en un archivo o visualizarse desde la pestaña View Event Log.

CSP Gateway tracing

Por último, se pueden rastrean las solicitudes y respuestas en la pestaña View HTTP Trace de CSP Gateway. Activa el rastreo y las solicitudes comenzarán a ser capturadas inmediatamente. No olvides apagarlo después de realizar la depuración. Este es un ejemplo de sesión de depuración:

Nota: utiliza el rastreo si puedes identificar cuál es el problema y reproducirlo fácilmente. Utiliza el registro para recoger estadísticas, elaborar perfiles de rendimiento, etc.

Además, la mayoría de los servidores web ofrecen herramientas de registro y seguimiento del rendimiento.

ISCLOG

CSP Gateway es útil para determinar problemas de red y hacer seguimiento del rendimiento, pero para registrar lo que ocurre dentro de Caché se necesitan otras herramientas. Una de las herramientas más versátiles es ISCLOG. Documentación.

Es un global que puede almacenar información sobre el procesamiento de las solicitudes actuales. Para iniciar el registro, ejecuta:

set ^%ISCLOG = 2

Y para terminar el registro, ejecuta:

set ^%ISCLOG = 0

Este es el ejemplo de una solicitud:

^%ISCLOG=0
^%ISCLOG("Data")=24
^%ISCLOG("Data",1)=$lb(2,"CSPServer","Header from CSP Size:3744 CMD:h IdSource:3","4664","FORMS","2017-06-07 10:49:21.341","%SYS.cspServer2","","")
^%ISCLOG("Data",1,0)="^h30000 "_$c(14,0,0)_"A"
^%ISCLOG("Data",2)=$lb(2,"CSPServer","[UpdateURL] Looking up: //localhost/forms/form/info path found: //localhost/forms/ Appl= "_$c(2,1,3,4)_"@"_$c(3,4,1,2,1,9,1)_"/forms/"_$c(2,1,3,4,1,2,1,2,1,2,4,3,4,1,2,1,9,1,7,1)_":%All"_$c(8,1)_"/forms"_$c(7,1)_"FORMS"_$c(2,1,2,1,3,4,1,2,1,2,1,3,4,1,2,1,4,4,132,3,3,4,2,3,4,2,2,1,4,4,16,14,2,4,3,4,1,3,4,1,2,1,3,4,1,2,1,16,1)_"Form.REST.Main"_$c(2,4,2,4),"4664","FORMS","2017-06-07 10:49:21.342","%CSP.Request.1","124","L3DfNILTaE")
^%ISCLOG("Data",3)=$lb(2,"CSPServer","[UpdateURL] Found cls: Form.REST.Main nocharsetconvert:  charset:UTF-8 convert charset:UTF8","4664","FORMS","2017-06-07 10:49:21.342","%CSP.Request.1","124","L3DfNILTaE")
^%ISCLOG("Data",4)=$lb(2,"CSPServer","[HTML] Determined request type","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer2","124","L3DfNILTaE")
^%ISCLOG("Data",4,0)=$lb("usesession",1,"i%Class","Form.REST.Main","i%Service","REST","NOLOCKITEM","","i%GatewayError","")
^%ISCLOG("Data",5)=$lb(2,"CSPSession","[%LoadData] Loading CSP session, nosave=0","4664","FORMS","2017-06-07 10:49:21.342","%CSP.Session.1","","L3DfNILTaE")
^%ISCLOG("Data",5,0)=$lb(900,,0,5567742244,$c(149)_"Ù"_$c(3)_"ó»à"_$c(127)_",½"_$c(149,10)_"\"_$c(18)_"v"_$c(128,135)_"3Vô"_$c(11)_"*"_$c(154)_"PÏG¥"_$c(140,157,145,10,131)_"*",2,"FORMS","001000000000L3DfNILTaE1cDBJNjyQdyLwKq4wCXP82ld8gic",,0,"ru","L3DfNILTaE",2,1,"/forms/",$lb("UnknownUser","%All,%Developer","%All,%Developer",64,-559038737),"","","","2017-06-07 10:48:51","2017-06-07 10:49:04","Basic ZGV2OjEyMw==","Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0","","",0,"/forms/","","","",4,"","","","","http://localhost:57772/formsui/index.html")
^%ISCLOG("Data",6)=$lb(2,"CSPServer","[CSPDispatch]Requested GET /forms/form/info","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE")
^%ISCLOG("Data",7)=$lb(2,"CSPServer","[CSPDispatch] ** Start processing request newSes=0","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE")
^%ISCLOG("Data",7,0)="/forms/form/info"
^%ISCLOG("Data",8)=$lb(2,"CSPServer","[CSPDispatch] Service type is REST has-soapaction=0 nosave=0","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE")
^%ISCLOG("Data",9)=$lb(2,"CSPServer","[CSPDispatch]About to run page: Form.REST.Main","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE")
^%ISCLOG("Data",9,0)=$lb("UnknownUser","%All,%Developer","%All,%Developer",64,-559038737)
^%ISCLOG("Data",10)=$lb(2,"CSPServer","[callPage] url=/forms/form/info ; Appl: /forms/ newsession=0","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE")
^%ISCLOG("Data",11)=$lb(2,"CSPServer","[callPage]Imported security context ; User: UnknownUser ; Roles: %All,%Developer","4664","FORMS","2017-06-07 10:49:21.342","%SYS.cspServer","124","L3DfNILTaE")
^%ISCLOG("Data",12)=$lb(2,"CSPServer","[OutputCSPGatewayData]: chd=1;","4664","FORMS","2017-06-07 10:49:21.431","%CSP.Response.1","","L3DfNILTaE")
^%ISCLOG("Data",13)=$lb(2,"CSPResponse","[WriteHTTPHeaderCookies] Session cookie: CSPSESSIONID-SP-57772-UP-forms-=001000000000L3DfNILTaE1cDBJNjyQdyLwKq4wCXP82ld8gic; path=/forms/;  httpOnly;","4664","FORMS","2017-06-07 10:49:21.431","%CSP.Response.1","124","L3DfNILTaE")
^%ISCLOG("Data",14)=$lb(2,"CSPServer","[callPage] Return Status","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","124","L3DfNILTaE")
^%ISCLOG("Data",14,0)=1
^%ISCLOG("Data",15)=$lb(2,"CSPServer","[OutputCSPGatewayData]: chd=1;","4664","FORMS","2017-06-07 10:49:21.431","%CSP.Response.1","","L3DfNILTaE")
^%ISCLOG("Data",16)=$lb(2,"CSPServer","[Cleanup]Page EndSession=0; needToGetALicense=-1; nosave=0; loginredirect=0; sessionContext=1","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","124","L3DfNILTaE")
^%ISCLOG("Data",17)=$lb(2,"CSPSession","[Cleanup] EndSession=0 nosave=0","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","124","L3DfNILTaE")
^%ISCLOG("Data",18)=$lb(2,"CSPSession","[%SaveData] Saved: ","4664","FORMS","2017-06-07 10:49:21.431","%CSP.Session.1","","L3DfNILTaE")
^%ISCLOG("Data",18,0)=$lb(900,,0,5567742261,$c(149)_"Ù"_$c(3)_"ó»à"_$c(127)_",½"_$c(149,10)_"\"_$c(18)_"v"_$c(128,135)_"3Vô"_$c(11)_"*"_$c(154)_"PÏG¥"_$c(140,157,145,10,131)_"*",2,"FORMS","001000000000L3DfNILTaE1cDBJNjyQdyLwKq4wCXP82ld8gic",,0,"ru","L3DfNILTaE",2,1,"/forms/",$lb("UnknownUser","%All,%Developer","%All,%Developer",64,-559038737),"","","","2017-06-07 10:48:51","2017-06-07 10:49:21","Basic ZGV2OjEyMw==","Mozilla/5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko/20100101 Firefox/53.0","","",0,"/forms/","","","",5,"","","","","http://localhost:57772/formsui/index.html")
^%ISCLOG("Data",19)=$lb(2,"CSPServer","[Cleanup] Restoring roles before running destructor","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","","L3DfNILTaE")
^%ISCLOG("Data",19,0)=$lb("UnknownUser","%All,%Developer","%All,%Developer",64,-559038737)
^%ISCLOG("Data",20)=$lb(2,"CSPServer","[Cleanup] End","4664","FORMS","2017-06-07 10:49:21.431","%SYS.cspServer","","L3DfNILTaE")
^%ISCLOG("Data",20,0)="&lt;-Finish processing request->"
^%ISCLOG("Data",21)=$lb(2,"GatewayRequest","[CSPGWClientRequest] GWID: ed-pc:57772; Request: sys_get_system_metricsTimeout: 5","11112","%SYS","2017-06-07 10:49:23.141","%SYS.cspServer3","","")
^%ISCLOG("Data",22)=$lb(2,"GatewayRequest","[CSPGWClientRequest] GWID: 127.0.0.1:57772; Request: sys_get_system_metricsTimeout: 5","11112","%SYS","2017-06-07 10:49:23.141","%SYS.cspServer3","","")
^%ISCLOG("Data",23)=$lb(2,"GatewayRequest","[SendSimpleCmd:Server:Failed] WebServer: 127.0.0.1:57772; Gateway Server Request Failed","11112","%SYS","2017-06-07 10:49:23.141","%CSP.Mgr.GatewayMgrImpl.1","","")
^%ISCLOG("Data",23,0)=0
^%ISCLOG("Data",24)=$lb(2,"GatewayRequest","[GetMetrics]","11112","%SYS","2017-06-07 10:49:23.141","%CSP.Mgr.GatewayMgrImpl.1","","")
^%ISCLOG("Data",24,0)="&lt;-End Request Client->"

Además, este es un script rápido para la salida de global a un archivo:

set p="c:\temp\isclog.txt"
open p:"NW"
use p zw ^%ISCLOG
close p

 

Custom logging

Aunque las herramientas de registro predeterminadas son bastante buenas, tienen varios problemas:

  • Son genéricas y no están familiarizadas con tu solicitud
  • Las opciones más detalladas afectan al rendimiento
  • No están bien estructuradas, por lo que puede resultar difícil extraer información

Por lo tanto, se pueden cubrir casos más específicos escribiendo sistemas de registro personalizados. Este es un ejemplo de una clase persistente que registra parte del objeto %request:

/// Incoming request
Class Log.Request Extends %Persistent
{
/// A string indicating HTTP method used for this request.
Property method As %String;
/// A string containing the URL up to and including the page name
/// and extension, but not including the query string.
Property url As %String(MAXLEN = "");
/// A string indicating the type of browser from which the request
/// originated, as determined from the HTTP_USER_AGENT header.
Property userAgent As %String(MAXLEN = "");
/// A string indicating the MIME Content-Type of the request.
Property contentType As %String(MAXLEN = "");
/// Character set this request was send in, if not specified in the HTTP headers
/// it defaults to the character set of the page it is being submitted to.
Property charSet As %String(MAXLEN = "");
/// A &lt;class>%CSP.Stream&lt;/class> containing the content submitted
/// with this request.
Property content As %Stream.GlobalBinary;
/// True if the communication between the browser and the web server was using
/// the secure https protocol. False for a normal http connection.
Property secure As %Boolean;
Property cgiEnvs As array Of %String(MAXLEN = "", SQLPROJECTION = "table/column");
Property data As array Of %String(MAXLEN = "", SQLPROJECTION = "table/column");
ClassMethod add() As %Status
{
    set request = ..%New()
    quit request.%Save()
}
Method %OnNew() As %Status [ Private, ServerOnly = 1 ]
{
    #dim %request As %CSP.Request
    #dim sc As %Status = $$$OK
    quit:'$isObject($g(%request)) $$$ERROR($$$GeneralError, "Not a web context")
    set ..charSet = %request.CharSet
    if $isObject(%request.Content) {
        do ..content.CopyFromAndSave(%request.Content)
    } else {
        set ..content = ""
    }
    set ..contentType = %request.ContentType
    set ..method = %request.Method
    set ..secure = %request.Secure
    set ..url = %request.URL
    set ..userAgent = %request.UserAgent
    set cgi = ""
    for {
        set cgi=$order(%request.CgiEnvs(cgi))
        quit:cgi=""
        do ..cgiEnvs.SetAt(%request.CgiEnvs(cgi), cgi)
    }
    // Only gets first data if more than one data with the same name is present
    set data = ""
    for {
        set data=$order(%request.Data(data))
        quit:data=""
        do ..data.SetAt(%request.Get(data), data)
    }
    quit sc
}
}

Para añadir un nuevo registro a la tabla Log.Request, agrega una llamada a tu código:

do ##class(Log.Request).add()

Es un ejemplo muy básico, puede y debe ampliarse para registrar comentarios, variables o cualquier otra cosa que puedas necesitar. La principal ventaja de este enfoque es la posibilidad de ejecutar consultas SQL sobre los datos registrados.  Para obtener más información sobre cómo crear tu propio sistema de registro, consulta este artículo.

Session events

La clase Event (Evento) es una clase que define las interfaces que se llaman durante la vida de un objeto %CSP.Session. Para utilizarla, hay que subclasificar %CSP.SessionEvents e implementar el código del método que quieres ejecutar. A continuación, en la configuración de la aplicación CSP, hay que establecer la clase de evento en la clase que has creado.

Las siguientes devoluciones de llamada están disponibles:

  • OnApplicationChange
  • OnEndRequest
  • OnEndSession
  • OnLogin
  • OnLogout
  • OnStartRequest
  • OnStartSession
    • OnTimeout

    Por ejemplo, los custom loggings comentado anteriormente pueden invocarse desde estos métodos.

    Output to device

    Una de las opciones más sencillas - un método de utilidad CSP que muestra todos los objetos como respuesta. Solo hay que añadir esto a cualquier parte de tu código:

    set %response.ContentType = "html"
    do ##class(%CSP.Utils).DisplayAllObjects()
    return $$$OK

     

    Conclusión

    Hay varias herramientas que se puede utilizar para depurar las aplicaciones web. Elige la más adecuada para la tarea que vayas a realizar.

      Y vosotros... ¿tenéis algún consejo o truco para depurar aplicaciones web desde Caché?

    0
    0 141
    Artículo Muhammad Waseem · oct 13, 2021 4m read

    A lo largo de los años, me he encontrado con la necesidad de crear varios mensajes HL7 basados en un solo mensaje entrante. Por lo general, toman la forma de un pedido o son el resultado de un laboratorio. Cada vez que he afrontado el reto, he intentado empezar de cero, con la convicción de que el intento anterior podría haberse hecho mejor.

    Recientemente, volvió a surgir la necesidad y pude crear una solución de la que no me avergonzaba. Mi principal preocupación era que siempre me encontraría enterrado en un BPL, o usaría ObjectScript e intentaría editar mensajes usando el método SetValueAt para la clase de mensaje HL7.

    Problema
    Cuando el Sistema A procesa múltiples pedidos para un solo paciente, el resultado vendrá en un solo mensaje con ORCgrp repetido con los segmentos OBR y OBX contenidos en este. El sistema B solo puede recibir un único OBR por mensaje.

    1
    1 388
    Artículo Kurro Lopez · jun 16, 2020 9m read

    En mi anterior artículo, revisamos los posibles casos de uso para macros, así que pasemos ahora a un ejemplo más completo de usabilidad de macros. En este artículo diseñaremos y crearemos un sistema de registro.

    Sistema de registro

    El sistema de registro es una herramienta útil para monitorear el trabajo de una aplicación que ahorra mucho tiempo durante la depuración y el monitoreo. Nuestro sistema constaría de dos partes:

    • Clase de almacenamiento (para registros de anotaciones)
    • Conjunto de macros que agregan automáticamente un nuevo registro al registro

    Clase de almacenamiento

    Vamos a crear una tabla de lo que necesitamos almacenar y especificar cuándo se pueden obtener estos datos, durante la compilación o en tiempo de ejecución. Esto será necesario cuando trabaje en la segunda parte del sistema: macros, donde buscaremos tener tantos detalles registrables como sea posible durante la compilación:

    InformaciónObtenido durante
    Tipo de eventoCompilación
    Nombre de claseCompilación
    Nombre del métodoCompilación
    Argumentos pasados ​​a un métodoCompilación
    Número de línea en el código fuente de clsRuntime
    Número de línea en el código int generadoRuntime
    Nombre de usuarioRuntime
    Date/TimeRuntime
    MensajeRuntime
    direccion IPRuntime

    Vamos a crear una clase App.Log que contenga las propiedades de la tabla anterior. Cuando se crea un objeto App.Log, las propiedades de UserName, TimeStamp y ClientIPAddress se completan automáticamente.

    App.Log class:

    Class App.Log Extends %Persistent
    {
    

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

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

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

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

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

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

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

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

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

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

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

     

    Macros de registro

    Por lo general, las macros se almacenan en archivos * .inc separados que contienen sus definiciones. Los archivos necesarios se pueden incluir en clases usando el comando Include MacroFileName, que en este caso se verá de la siguiente manera: Include App.LogMacro.
     
    Para comenzar, definamos la macro principal que el usuario agregará al código de su aplicación:

    #define LogEvent(%type, %message) Do ##class(App.Log).AddRecord($$$CurrentClass, $$$CurrentMethod, $$$StackPlace, %type, $$$MethodArguments, %message)

    Esta macro acepta dos argumentos de entrada: Tipo de evento y Mensaje. El usuario define el argumento Mensaje, pero el parámetro Tipo de evento requerirá macros adicionales con diferentes nombres que identificarán automáticamente el tipo de evento:

    #define LogNone(%message)         $$$LogEvent("NONE", %message)
    #define LogError(%message)        $$$LogEvent("ERROR", %message)
    #define LogFatal(%message)        $$$LogEvent("FATAL", %message)
    #define LogWarn(%message)         $$$LogEvent("WARN", %message)
    #define LogInfo(%message)         $$$LogEvent("INFO", %message)
    #define LogStat(%message)         $$$LogEvent("STAT", %message)
    #define LogDebug(%message)        $$$LogEvent("DEBUG", %message)
    #define LogRaw(%message)          $$$LogEvent("RAW", %message)

    Por lo tanto, para realizar el registro, el usuario solo necesita colocar la macro $$$LogError("Additional message") en el código de la aplicación.
    Todo lo que necesitamos hacer ahora es definir las macros $$$CurrentClass, $$$CurrentMethod, $$$StackPlace, $$$MethodArguments.Comencemos con las tres primeras:

    #define CurrentClass     ##Expression($$$quote(%classname))
    #define CurrentMethod    ##Expression($$$quote(%methodname))
    #define StackPlace       $st($st(-1),"PLACE")

    %classname, %methodname las variables se describen en la documentacion. La función $stack devuelve el número de línea del código INT. Para convertirlo en un número de línea CLS, podemos usar este código.

    Usemos el paquete %Dictionary para obtener una lista de argumentos de métodos y sus valores. Contiene toda la información sobre las clases, incluidas las descripciones de los métodos. Estamos particularmente interesados ​​en la clase %Dictionary.CompiledMethod y su propiedad FormalSpecParsed, que es una lista:

    $lb($lb("Name","Classs","Type(Output/ByRef)","Default value "),...)

    correspondiente a la firma del método. Por ejemplo:

    ClassMethod Test(a As %Integer = 1, ByRef b = 2, Output c)

    tendrá el siguiente valor FormalSpecParsed:

    $lb(
    $lb("a","%Library.Integer","","1"),
    $lb("b","%Library.String","&","2"),
    $lb("c","%Library.String","*",""))

    Necesitamos que la macro $$$MethodArguments se expanda en el siguiente código (para el método Test):

    "a="_$g(a,"Null")_"; b="_$g(b,"Null")_"; c="_$g(c,"Null")_";"

    Para lograr esto, tenemos que hacer lo siguiente durante la compilación:

    1. Obtenga un nombre de clase y un nombre de método
    2. Abra una instancia correspondiente de la clase %Dictionary.CompiledMethod y obtenga su propiedad FormalSpec
    3. Conviértalo en una línea de código fuente

    Agreguemos los métodos correspondientes a la clase App.Log:

    ClassMethod GetMethodArguments(ClassName As %String, MethodName As %String) As %String
    {
        Set list = ..GetMethodArgumentsList(ClassName,MethodName)
        Set string = ..ArgumentsListToString(list)
        Return string
    }
    

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

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


    Ahora definamos la macro $$$MethodArguments como:

    #define MethodArguments ##Expression(##class(App.Log).GetMethodArguments(%classname,%methodname))

    Caso de uso

    A continuación, creemos una clase App.Use con un método de prueba para demostrar las capacidades del sistema de registro:

    Include App.LogMacro
    Class App.Use [ CompileAfter = App.Log ]
    {
    /// Do ##class(App.Use).Test()
    ClassMethod Test(a As %Integer = 1, ByRef b = 2)
    {
        $$$LogWarn("Text")
    }
    }

    Como resultado, la macro $$$LogWarn("Text") en el código int se convierte en la siguiente línea:

    Do ##class(App.Log).AddRecord("App.Use","Test",$st($st(-1),"PLACE"),"WARN","a="_$g(a,"Null")_"; b="_$g(b,"Null")_";", "Text")

    La ejecución de este código creará un nuevo registro de App.Log:

    Mejoras

    Después de haber creado un sistema de registro, aquí hay algunas ideas de mejora:

    • En primer lugar, existe la posibilidad de procesar argumentos de tipo objeto ya que nuestra implementación actual solo registra objetos oref.
    • Segundo, una llamada para restaurar el contexto de un método a partir de valores de argumentos almacenados.

    Procesamiento de argumentos de tipo objeto

    La línea que pone un valor de argumento en el registro se genera en el método ArgumentsListToString y tiene este aspecto:

    "_$g(" _ $lg($lg(List,i)) _ ","_$$$quote(..#Null)_")_"

    Realicemos una refactorización y muévala a un método GetArgumentValue separado que acepte un nombre y clase de variable (todo lo que sabemos de FormalSpecParsed) y genere un código que convertirá la variable en una línea. Usaremos el código existente para los tipos de datos, y los objetos se convertirán en JSON con la ayuda de los métodos SerializeObject (para llamar desde el código de usuario) y WriteJSONFromObject (para convertir un objeto en JSON):

    ClassMethod GetArgumentValue(Name As %String, ClassName As %Dictionary.CacheClassname) As %String
    {
        If $ClassMethod(ClassName, "%Extends", "%RegisteredObject") {
            // it's an object
            Return "_##class(App.Log).SerializeObject("_Name _ ")_"
        } Else {
            // it's a datatype
            Return "_$g(" _ Name _ ","_$$$quote(..#Null)_")_"
        }
    }
    

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

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

        //Redirect IO to the current routine - makes use of the labels defined below
        Use $io::("^"_$ZNAME)
    
        //Enable redirection
        Do ##class(%Device).ReDirectIO(1)
    
        Do ##class(%ZEN.Auxiliary.jsonProvider).%ObjectToJSON(Object)
    } Catch Ex {
        Set Str = ""
    }
    
    //Return to original redirection/mnemonic routine settings
    If (OldMnemonic '= "") {
        Use OldIO::("^"_OldMnemonic)
    } Else {
        Use OldIO
    }
    Do ##class(%Device).ReDirectIO(OldIORedirected)
    
    Quit Str
    
    // Labels that allow for IO redirection
    // Read Character - we don't care about reading
    

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

    Una entrada de registro con un argumento de tipo objeto se ve así:

    Restaurando el contexto

    La idea de este método es hacer que todos los argumentos estén disponibles en el contexto actual (principalmente en la terminal, para la depuración). Para este fin, podemos usar el parámetro del método ProcedureBlock. Cuando se establece en 0, todas las variables declaradas dentro de dicho método permanecerán disponibles al salir del método. Nuestro método abrirá un objeto de la clase App.Log y deserializará la propiedad Argumentos.

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

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

    Así es como se ve en la terminal:

    >zw
    >do ##class(App.Log).LoadContext(2)
    >zw
    

    a=1 b=<OBJECT REFERENCE>[2@%ZEN.proxyObject]

    >zw b b=<OBJECT REFERENCE>[2@%ZEN.proxyObject] +----------------- general information --------------- | oref value: 2 | class name: %ZEN.proxyObject | reference count: 2 +----------------- attribute values ------------------ | %changed = 1 | %data("prop1") = 123 | %data("prop2") = "abc" | %index = ""

    ¿Que sigue?

    La mejora potencial clave es agregar otro argumento a la clase de registro con una lista arbitraria de variables creadas dentro del método.

    Conclusiones

    Las macros pueden ser bastante útiles para el desarrollo de aplicaciones.

    Preguntas

    ¿Hay alguna manera de obtener el número de línea durante la compilación?

    Links

    1
    0 242
    Artículo Alberto Fuentes · ene 16, 2023 2m read

    cAdvisor (abreviatura de contenedor Advisor) analiza y muestra el uso de recursos y los datos de rendimiento desde los contenedores en ejecución. cAdvisor ya viene preparado para publicar métricas en formato Prometheus. 

    https://prometheus.io/docs/guides/cadvisor/

    Prometheus está integrado en SAM. Esto permite aprovechar las métricas de cAdvisor y mostrarlas a través de Prometheus y Grafana.

    Como cAdvisor escucha en el puerto 8080, que entra en conflicto con el puerto de Nginx, puedes cambiar el puerto Nginx para solucionarlo:

    Pasos para realizar la configuración:

    1. Cambia el puerto nginx.

    1
    0 489
    Artículo Jose-Tomas Salvador · ene 28, 2020 5m read

    Hola Desarrolladores!

    "objectscript.conn" :{
          "ns": "IRISAPP",
          "active": true,
          "docker-compose": {
            "service": "iris",
            "internalPort": 52773
          }

    Quiero compartir con vosotros una estupenda nueva funcionalidad que he descubierto en la nueva versión 0.8 del plugin de VSCode ObjectScript desarrollado por @Dmitry.Maslennikovy CaretDev.

    La versión viene con un nuevo parámetro de configuración: "docker-compose"; que resuelve el problema con los puertos que necesitas para conectar tu editor VSCode a IRIS. No era muy cómodo si tenías más de un contenedor docker con IRIS corriendo en la misma máquina. Ahora ¡esto está resuelto!

    Veamos como funciona ahora...

    1
    0 216
    Artículo Ricardo Paiva · jul 28, 2022 3m read

    Estos días he estado trabajando con la excelente y nueva funcionalidad: LOAD DATA. Con este artículo me gustaría compartir mis primeras experiencias con todos. Los siguientes puntos no contienen ningún orden ni ningún otro análsis. Son solo cosas que observé al utilizar el comando LOAD DATA. Y se debe tener en cuenta que estos puntos se basan en la versión 2021.2.0.617 de IRIS, que es una versión de prueba. Por ello, es posible que mis observaciones no apliquen a las nuevas versiones de IRIS. Pero quizás sean útiles para otros.

    1) La ruta del archivo está en el lado del servidor

    He hecho mis primeras pruebas mediante JDBC. La primera sorpresa que me encontré: ¡El archivo y la ruta del archivo deben, por supuesto ;-) estar en el lado del servidor! El controlador JDBC no se encarga de esto en el lado del cliente. Probablemente esto es obvio, pero no lo había considerado al principio.

    2) El sufijo del archivo no es relevante

    Los documentos dicen:

    "Los nombres de los archivos deben incluir un sufijo .txt o .csv (valores separados por comas)."

    Según mis observaciones, el comportamiento no es así. El sufijo no es relevante.

    3) ¡Lee los documentos! ... o ¿dónde están las filas con errores?

    Cuando cargué algunos archivos de datos, perdí filas. Si hay algún problema con una línea, la línea se ignora. Esto sucede silenciosamente en segundo plano y no se notifica activamente al cliente. Después de consultar https://https//youtu.be/jm7bDK0FoiI me di cuenta de que tengo que revisar %SQL_Diag.Result y %SQL_Diag.Message para ver los problemas de forma detallada. También me di cuenta de que este comportamiento ya está descrito en esta página: https://docs.intersystems.com/iris20212/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_loaddata ... así que hay que leer los manuales ;-)

    Algunos ejemplos de lo que se puede ver:

    SELECT * FROM %SQL_Diag.Result ORDER BY createTime DESC
    
    

    Revisa la columna errorCount de tu descarga.

    Se pueden ver los detalles (de la fila) en %SQL_Diag.Message

    SELECT * FROM %SQL_Diag.Message ORDER BY messageTime DESC

    Se puede filtrar por un diagResult específico (%SQL_Diag.Result.ID = %SQL_Diag.Message.diagResult)

    SELECT * FROM %SQL_Diag.Message
    WHERE diagResult=4
    ORDER BY messageTime DESC
    

    4) LOAD DATA no es compatible con $SYSTEM.SQL.Schema.ImportDDL

    Para mi aplicación de prueba Openflights Dataset intenté cargar todos los archivos externos con LOAD DATA. Las sentencias se agrupan dentro de un archivo de texto (sql) donde anteriormente también creé las tablas.

    Aprendí que no se puede hacer eso mediante $SYSTEM.SQL.Schema.ImportDDL.

    Por cierto, la documentación de ImportDDL señala que no todas las sentencias SQL son compatibles. Solo unas pocas sentencias SQL aparecen en esta página.

    LOAD DATA lamentablemente no es una de ellas... Y, por cierto, USE DATABASE tampoco.

    5) Para administrar unicode hay que cambiar una configuración

    Para evitar problemas con el código de los datos durante la carga, hay que poner esta configuración en el servidor de %Java: -Dfile.encoding=UTF-8
    Consulta más detalles en esta publicación. Este problema debería desaparecer en la próxima versión de IRIS.

    6) El proceso de carga se detiene con un error, pero los datos se cargan

    La carga de datos mediante JDBC se para por un error %qparsets. Se ve así:

    Error: [SQLCODE: &lt;-400>:&lt;Fatal error occurred>]
    [Error: &lt;&lt;UNDEFINED>zExecute+83^%sqlcq.OPENFLIGHTS.cls10.1 *%qparsets>]
    [Location: &lt;ServerLoop>]

    Pero hay que preocuparse, los datos se cargaron :-) Consulta más detalles en esta publicación.
    Este problema debería desaparecer en la próxima versión de IRIS.

    1
    0 260
    Artículo Pierre-Yves Duquesnoy · jun 16, 2020 26m read

    Introducción

    InterSystems IRIS 2020.1 incluye PEX (Production EXtension Framework), para facilitar el desarrollo de producciones de Interoperabilidad de IRIS con componentes escritos en Java o .NET.

    Gracias a PEX, un desarrollador de integraciones con conocimientos Java o .NET puede beneficiarse de la potencia, escalabilidad y robustez del framework de Interoperabilidad de InterSystems IRIS, y ser productivo en muy poco tiempo.

    1
    0 595
    Artículo Alberto Fuentes · mayo 6, 2021 4m read

    ¡Hola desarrolladores!

    Supón que tienes una clase persistente con datos y quieres tener una interfaz de usuario Angular sencilla, para ver los datos y llevar a cabo operaciones CRUD.

    En este artículo describíamos cómo desarrollar una interfaz de usuario Angular para InterSystems IRIS utilizando RESTForms2.

    A continuación vamos a explicar cómo conseguir una interfaz de usuario Angular sencilla que permita realizar operaciones CRUD y ver automáticamente los datos de tu clase de InterSystems IRIS en menos de 5 minutos.

    ¡Vamos!

    Necesitarás:

    1. InterSystems IRIS
    2. ZPM
    3. Los módulos RESTForms2 y RESTForms2-UI.

    Utilizaré como ejemplo la clase Data.Countries que generé e importé a través de csvgen, utilizando este comando:

    d ##class(community.csvgen).GenerateFromURL("https://raw.githubusercontent.com/datasciencedojo/datasets/master/WorldDBTables/CountryTable.csv",",","Data.Countries"
    

    Para crear una interfaz de usuario Angular necesitamos exponer una API REST para esta clase, que dará servicio a las operaciones CRUD.

    Vamos a utilizar el módulo restforms2 para esto. 

    Este comando en dockerfile instala restforms2 en el contenedor IRIS:

    zpm "install restforms2" \

    Para añadir una API REST, necesitamos hacer que nuestra clase de ejemplo herede de Form.Adaptor:

    Class Data.Countries Extends (%Library.Persistent, Form.Adaptor)
    

    Añade los parámetros restforms2 a la clase persistente para gestionar el comportamiento general: ordenación, visualización, etc:

    // Form name, not a global key so it can be anything
    Parameter FORMNAME = "Countries";
    
    /// Default permissions
    /// Objects of this form can be Created, Read, Updated and Deleted
    /// Redefine this parameter to change permissions for everyone
    /// Redefine checkPermission method (see Form.Security) for this class 
    /// to add custom security based on user/roles/etc.
    Parameter OBJPERMISSIONS As %String = "CRUD";
    
    /// Property used for basic information about the object
    /// By default getObjectDisplayName method gets its value from it
    Parameter DISPLAYPROPERTY As %String = "name";
    

    Perfecto. A continuación, podemos utilizar la sintaxis de restforms2 para hacerle saber qué propiedades de la clase queremos exponer a la API CRUD. Puedes hacerlo añadiendo el atributo "DISPLAYNAME =" a las propiedades que quieras exponer en restforms2-ui. Por ejemplo:

    Property code As %Library.String(MAXLEN = 250) [ SqlColumnNumber = 2 ];
    
    Property name As %Library.String(DISPLAYNAME = "Name", MAXLEN = 250) [ SqlColumnNumber = 3 ];
    
    Property continent As %Library.String(DISPLAYNAME = "Continent", MAXLEN = 250) [ SqlColumnNumber = 4 ];
    
    Property region As %Library.String(DISPLAYNAME = "Region", MAXLEN = 250) [ SqlColumnNumber = 5 ];
    
    Property surfacearea As %Library.Integer(DISPLAYNAME = "Surface Area", MAXVAL = 2147483647, MINVAL = -2147483648) [ SqlColumnNumber = 6, SqlFieldName = surface_area ];
    
    Property independenceyear As %Library.Integer(DISPLAYNAME = "Independence Year", MAXVAL = 2147483647, MINVAL = -2147483648) [ SqlColumnNumber = 7, SqlFieldName = independence_year ];
    

    ¡Genial! Ahora vamos a introducir la capa de interfaz de usuario. Este comando en dockerfile instala restforms2-ui, que es la interfaz de usuario Angular para Restform2:

    zpm "install restforms2-ui" \

    ¡Y ya está! Vamos a echarle un vistazo a la interfaz de usuario de nuestra clase de ejemplo, que puedes encontrar en la URL server:port/restforms2-ui:

    RESTForms incluye las clases de prueba Person y Company, y se pueden utilizar para examinar las características de restfomsUI. Actualmente, puedes editar campos de cadenas de texto, numéricos, booleanos, de fecha y búsqueda.

    Puedes probar todo esto en tu portátil, si clonas y construyes este repositorio:

    docker-compose up -d --build

    Y, a continuación, abres la URL:

    localhost:port/restforms2-ui/index.html

    o si usas VSCode, selecciona este elemento del menú:

    ¡Happy coding!

    1
    0 323
    Artículo Yuri Marx · mar 7, 2022 4m read

    InterSystems IRIS es una gran plataforma de datos y posee las funcionalidades que demanda el mercado. En este artículo, recojo las 10 mejores:

    PuestoFuncionalidad¿Por qué?Más información
    1Análisis adaptativoAnalizar los datos empresariales actuales "TAL CUAL" y en tiempo real, con menos esfuerzo de modelado, mayor velocidad de ejecución y soporte para los principales visores analíticos (incluyendo Excel) es un factor clave para la estrategia corporativa de las empresas.

    Enlace

    2Administrador de APILos activos digitales se consumen mediante la API REST. Se requiere controlar la reutilización, seguridad, consumo, catálogo de activos, ecosistema de desarrolladores y otros aspectos en un punto central. El Administrador de API es la herramienta adecuada para hacerlo. Por ello, todas las empresas tienen o quieren tener un administrador de API.Enlace
    3Sharding de la base de datosLa cantidad total de datos creados, capturados, copiados y consumidos a nivel mundial aumenta rápidamente. Hasta 2025, se prevé que la creación de datos a nivel mundial crezca hasta más de 180 zettabytes. En este escenario, es crítico para las empresas poder procesar los datos de forma distribuida (en shards, como hadoop o mongodb), para aumentar y mantener el rendimiento. El otro aspecto importante es que IRIS es 3 veces más rápido que Caché y más rápido que las bases de datos de AWS, en la nube de AWS.

    Enlace 1

    Enlace 2

    4Soporte a PythonPython es el lenguaje más popular para hacer Inteligencia Artificial (IA). Y la IA está en el centro de la estrategia empresarial, porque permite obtener nuevos conocimientos, conseguir más productividad y reducir costes.

    Enlace 1

    Enlace 2

    Enlace 3

    5Las APIs nativas (Java, .NET, Node.js, Python) y PEXEn EE. UU. hay casi un millón de ofertas de puestos de trabajo en TI (fuente: https://www.forbes.com/sites/stuartanderson/2021/03/18/over-1-million-job-vacancy-postings-in-computer-occupations-in-us/). Es muy difícil encontrar un desarrollador de ObjectScript. Por eso, es importante ser capaz de utilizar las funcionalidades de IRIS, como la interoperabilidad, con el lenguaje de programación oficial del equipo de desarrolladores (Python, Java, .NET, etc.).

    Enlace 1

    Enlace 2

    Enlace 3

    Enlace 4

    Enlace 5

    Enlace 6

    6Interoperabilidad, FHIR y IoT

    Las empresas se conectan e intercambian datos constantemente. Los departamentos también deben trabajar conectados seguir procesos con mayor valor estratégico y menor coste. La mejor tecnología para ello son las herramientas de interoperabilidad, especialmente ESB, adaptadores de integración, motores de automatización de procesos empresariales (BPL), herramientas de transformación de datos (DTL) y la adopción de estándares de interoperabilidad del mercado, como FHIR y MQTT/IoT. La interoperabilidad de InterSystems soporta todo esto (para FHIR utiliza IRIS for Health).

    Enlace 1

    Enlace 2

    Enlace 3

    7Cloud, Docker y MicroserviciosAhora todo el mundo quiere una arquitectura de microservicios en la nube. Quieren romper los monolitos para crear proyectos más pequeños, menos complejos, menos acoplados, más escalables, reutilizables e independientes. IRIS permite implementar microservicios de datos, aplicaciones y análisis, gracias a la compatibilidad de IRIS con shards, docker, kubernetes, computación distribuida, herramientas DevOps y menor consumo de CPU/memoria (¡IRIS soporta incluso procesadores de ARM!). Pero los microservicios requieren que la gestión de las APIs de los microservicios, usando el Administrador de API, se utilice de forma alineada con las empresas.

    Enlace 1

    Enlace 2

    Enlace 3

    Enlace 4

    Enlace 5

    8Informes integradosLos informes son cruciales para las empresas. El personal operativo y los responsables de la toma de decisiones utilizan los informes para respaldar su trabajo. Además, se entregan muchos informes a los clientes. Pero, con las aplicaciones móviles, y las aplicaciones de microservicios, es necesario habilitar los informes integrados. IRIS Reports soporta todo esto.Enlace
    9Soporte para VSCodeVSCode es el IDE más popular e InterSystems IRIS tiene un buen conjunto de herramientas para usarlo.

    Enlace 1

    Enlace 2

    Enlace 3

    10Ciencia de DatosLa capacidad de aplicar la ciencia de datos a las solicitudes y respuestas de integración y transacción de datos, utilizando Python, R e IntegratedML (AutoML) permiten que la IA sea inteligente en este momento que requieren las empresas. InterSystems IRIS proporciona IA con Python, R e IntegratedML (AutoML)

    Enlace 1

    Enlace 2

    Enlace 3

    1
    0 576
    Artículo Ricardo Paiva · dic 26, 2022 4m read

    Se han publicado en la Comunidad varios artículos muy útiles que muestran cómo usar Grafana con IRIS (o Cache/Ensemble) usando una base de datos intermedia.

    Pero yo quería llegar directamente a las estructuras de IRIS. En particular, quería acceder a los datos del Cache History monitor, que es accessible a través de SQL, como se describe aquí:

    https://community.intersystems.com/post/apm-using-cach%C3%A9-history-monitor

    y no quería nada entre los datos y yo.

    1
    0 177
    Artículo Luis Angel Pérez Ramos · dic 14, 2022 2m read

    En este artículo, mostraré cómo subir y descargar archivos vía http desde los productos de InterSystems.

    A menudo desde la comunidad surgen preguntas sobre como trabajar con archivos a través de http y normalmente os emplazamos a revisar el proyecto FileServer, que muestra la subida/descarga de archivos. Pero me gustaría hablar un poco más de cómo podemos enviar y recibir archivos desde los productos de InterSystems.

    Cómo descargar un archivo

    Si tenéis un archivo en un sistema de archivos y conocéis su ruta, podéis enviarlo vía REST o en el contexto de CSP llamando a este método:

    1
    0 992
    Artículo Muhammad Waseem · jul 15, 2022 5m read

    ¡Hola Comunidad!

    En este artículo voy a explicar cómo acceder a la información y a las tablas del dashboard (cuadro de mando) del sistema del Portal de Administración mediante el uso de Python Embebido.

    1
    0 228
    Artículo Alberto Fuentes · dic 9, 2021 7m read

    ¡Hola a todos! Comparto hoy con vosotros un artículo muy interesante de Dmitry Maslennikov acerca de una nueva funcionalidad de GitHub llamada GitHub Codespaces. Permite ejecutar VSCode en el navegador, con casi la misma potencia con la que se ejecutaría de forma local en tu equipo, pero con la potencia de la nube, por lo que podrás elegir el tipo de equipo con hasta 32 núcleos de CPU y 64 GB de RAM.

    Parece impresionante, ¿verdad? Pero, ¿cómo nos podría ayudar a trabajar con proyectos realizados con InterSystems IRIS? Vamos a ver cómo configurarlo.

    Un inicio sencillo para cualquier repositorio

    Con esta funcionalidad, podrás editar cualquier repositorio en la nube, mediante el botón Code. Ten en cuenta que esta función aún está en fase beta, y podría no estar disponible para todos, y después del periodo beta solo estará disponible para cuentas de pago.

    Así que este repositorio no se ha preparado especialmente para Codespaces, solo para VSCode. Haz clic en el botón New codespace, para crear un entorno solo para ti.

    En mi caso, en el uso anterior de codespaces, ya había instalado la extensión ObjectScript y la había habilitado de forma global mediante línea de comando desde Codespaces. Por lo tanto, me lo instala cada vez, y el código ObjectScript ya se resaltó. Pero IRIS aún no está disponible. Empecemos con docker-compose.

    Después de eso, ahora podremos conectarnos al terminal de IRIS, y compilar el código

    Los puertos de docker-compose se reconocen automáticamente y se pueden abrir en el navegador, por lo que también es posible abrir el Portal de Administración del Sistema

    Uso con repositorio preparado

    Logramos ejecutar VSCode e IRIS en la nube, pero tuvimos que hacer algunas cosas manualmente para que estuviera listo. Pero también es posible hacer que tu repositorio esté listo para el desarrollo desde el inicio.

    Esto es posible, con devcontainer.json. Mostraré un ejemplo basado en el proyecto Realworld. Este proyecto es bastante complejo, tiene un backend y un frontend y utiliza docker-compose para empezar todo a la vez. 

    devcontainer también puede utilizar docker-compose, así que mi configuración es algo así:

    {
      "name": "IRIS RealWorld example",
      "dockerComposeFile": "docker-compose.yml",
      "service": "server",
      "extensions": [
        "intersystems-community.vscode-objectscript"
      ],
      "forwardPorts": [
        80,
        52773
      ],
      "workspaceFolder": "/home/irisowner/conduit",
      "remoteUser": "irisowner",
      "postCreateCommand": "iris start iris",
      "settings": {
        "terminal.integrated.defaultProfile.linux": "bash",
        "terminal.integrated.profiles.linux": {
          "bash": {
            "path": "bash",
            "icon": "terminal-bash"
          },
          "iris": {
            "path": "iris",
            "args": ["session", "iris"]
          }
        },
        "intersystems.servers": {
          "/ignore": true
        },
        "objectscript.ignoreInstallServerManager": true,
        "objectscript.ignoreInstallLanguageServer": true,
        "objectscript.conn": {
          "active": true,
          "host": "localhost",
          "port": 52773,
          "ns": "CONDUIT",
          "username": "demo",
          "password": "demo",
          "links": {
            "Conduit APP": "http://localhost:80/",
            "Conduit API": "http://${host}:${port}/conduit/"
          }
        }
      }
    }
    

    Hay varias cosas configuradas:

    • La ruta a docker-compose.yml personalizado, en especial para Codespaces
    • El nombre para el servicio principal, donde está el desarrollo 
    • La lista de extensiones instaladas por defecto en VSCode
    • Los puertos que se deben publicar, en este caso, el puerto del servidor web para IRIS y para el frontend
    • La ruta al directorio de trabajo
    • El usuario dentro del contenedor, como estamos entrando en el contenedor de IRIS, necesitamos el usuario irisowner 
    • En docker-compose nuestro contenedor IRIS se configuró para no utilizar el entrypoint iris-main de forma predeterminada, sino simplemente suspenderlo con Infinity, y después de iniciar el entorno, debemos iniciar nuestro IRIS
    • Y por último, la configuración de VSCode, también se puede ajustar aquí, es el nivel de configuración del equipo. Que por supuesto se puede anular o añadir con .vscode/settings.json

    Poner en marcha Codespaces para dicho repositorio llevará un poco más de tiempo, ya que necesitará crear todos los contenedores necesarios e iniciarlos. GitHub afirma que será posible preconfigurar dichas imágenes después de realizar cualquier actualización en el repositorio, por lo que el inicio será más rápido.

    Cuando comience, no se necesitarán más acciones, estará listo para el desarrollo

    Este proyecto tiene una opción para probar la API REST con pruebas preparadas de Postman, así que he instalado npm y newman dentro del contenedor de backend con IRIS. Es posible realizar estas pruebas allí. Todo fue aprobado, bien hecho.

    La parte del frontend está disponible

    GitHub también permite conectar a Codespaces desde el VSCode local. Cuando se pulsa el área verde en la esquina de Codespaces, se puede elegir abrir en VS Code (deberá estar instalada la extensión GitHub Codespaces)

    Y aquí está, el mismo proyecto, abierto con tu VSCode local, pero que se ejecuta en la nube, como se puede ver el resultado de ifconfig, no estoy en Singapur en este momento :)

    En el navegador pero sin los Codespaces de GitHub

    Qué pasa si no tienes acceso a la funcionalidad de Codespaces o no quieres usarla de esta manera, pero aun así te gustaría probar VSCode en el navegador.

    Bien, es posible con otro proyecto code-server

    Basta con ejecutar este VSCode con este comando

    docker run -it -p 8080:8080 codercom/code-server --auth=none

    Se ejecutará la versión predeterminada de VSCode, sin carpetas mapeadas en su interior. Simplemente organiza cualquier carpeta, y establécela como el directorio de trabajo workdir y la verás en su interior. 

    docker run -it -p 8080:8080 -v `pwd`:/opt/realworld -w /opt/realworld codercom/code-server --auth=none

    Es el VSCode predeterminado, sin la extensión instalada de ObjectScript. Tiene una limitación en cuanto a las extensiones, no tiene acceso al marketplace original de VSCode, sino que utiliza otro sitio, open-vsx.org, y la extensión principal de ObjectScript también está disponible allí.

    Con un Dockerfile como este podríamos hacer nuestro propio Code Server, con cualquier cosa instalada allí, así como algunas extensiones ya instaladas

    FROM codercom/code-server
    
    USER root
    
    RUN curl -fsSL https://deb.nodesource.com/setup_15.x | bash - && \
        apt-get install -y jq nodejs python3-pip python3-dev unixodbc-dev && \
        rm -rf /var/lib/apt/lists/* && \
        pip3 install pyodbc && \
        npm install -g yarn && \
      sudo chown -R 1000:1000 /home/coder
    
    COPY extensions extensions
    
    COPY settings.json /root/.local/share/code-server/User/settings.json
    
    ENV SERVICE_URL=https://open-vsx.org/vscode/gallery
    ENV ITEM_URL=https://open-vsx.org/vscode/item
    
    RUN \
      code-server --install-extension ms-python.python && \
      code-server --install-extension intersystems-community.vscode-objectscript && \
      find extensions -type f -exec code-server --install-extension {} \;
    
    WORKDIR /opt/intersystems
    
    CMD [ "--auth=none", "--disable-telemetry" ]
    

    Puedes definir algunos ajustes predeterminados settings.json para el nivel de usuario y si algunas extensiones que necesita no están disponibles en open-vsx, descárgalas manualmente, colócalas en la carpeta de extensiones junto a Dockerfile, y también las tendrá instaladas.

    Ahora puedes ejecutar el nuevo code-server con todas las extensiones instaladas que necesites

    docker run -it -p 8080:8080 -v `pwd`:/opt/realworld -w /opt/realworld &lt;strong>caretdev/code-server&lt;/strong> --auth=none
    

    Además, ya hay una sintaxis resaltada, lo único que falta es ejecutar el propio IRIS. Esto se puede hacer con docker-compose ampliado, donde el code-server será como un servicio más junto a IRIS

    1
    0 249
    Artículo Alberto Fuentes · jun 15, 2022 2m read

    ¡Hola desarrolladores! Quería compartir hoy un ejemplo muy interesante por parte de Tani Frankel. Se trata de una aplicación sencilla sobre la utilidad SystemPerfomance.

    Repasando nuestra documentación sobre la rutina de monitorización ^SystemPerformance (conocida como ^pButtons en versiones anteriores a IRIS), un cliente me dijo «Entiendo todo esto pero ojalá fuese más simple, más sencillo para definir perfiles y gestionarlos, etc.».

    Entonces pensé que sería interesante como ejercicio facilitar una pequeña interfaz para hacer esas tareas más sencillas.

    El primer paso era envolver en una API basada en clases la rutina actual de ^SystemPerformance.

    1
    0 524
    Artículo Ricardo Paiva · jun 4, 2021 4m read

    Durante las últimas semanas, el equipo de Solution Architecture (Soluciones de Arquitectura) ha estado trabajando para terminar la carga de trabajo de 2019: esto incluyó la creación del código abierto de la Demostración de Readmisiones que llevó a cabo HIMSS el año pasado, para poder ponerla a disposición de cualquiera que busque una forma interactiva de explorar las herramientas proporcionadas por IRIS.

    Durante el proceso de creación del código abierto de la demostración, nos encontramos de inmediato con un error crítico. Los datos subyacentes de los pacientes que se utilizaron para crear la demo no podían utilizarse como parte de un proyecto de código abierto porque no eran propiedad de InterSystems, eran propiedad de nuestro socio Baystate Health.

    Nuestro equipo estaba en un pequeño aprieto y tenía que encontrar una forma de sustituir los datos originales por datos sintéticos que pudieran utilizarse, pero manteniendo la "historia" de las demos, o su funcionalidad subyacente, consistente. Dado que la demo muestra cómo IRIS admite el workflow de machine learning de un científico de datos, había un nivel de complejidad añadido porque cualquier dato que utilizáramos tenía que ser lo suficientemente realista como para poder apoyar nuestro modelo de investigación. Después de una breve investigación, Synthea vino a nuestro rescate.

    Synthea es un generador de pacientes sintéticos, que modela sus historiales médicos. Es de código abierto. Synthea proporciona datos de alta calidad, realistas, pero no reales, de pacientes; en una variedad de formatos (incluido FHIR), con diferentes niveles de complejidad, cubriendo todos los aspectos de la atención médica. Los datos obtenidos no tienen coste, ni privacidad ni restricciones de seguridad, lo que permite investigar con datos de salud que de otra manera no estarían disponibles, de forma legal o práctica.

    Después de una investigación inicial, se eligió Synthea como la herramienta para solucionar nuestro problema de datos. Synthea es una herramienta increíble; sin embargo, un problema que encontramos fue que, para ejecutar el software y obtener los pacientes, teníamos que instalar varias dependencias en nuestros equipos.

    • Java JDK 1.8
    • Gradle Build Tool

    Cuando trabajas por tu cuenta, esto generalmente no es un problema, pero como nuestro equipo está formado por varias personas, es importante que todos puedan actualizarse con un nuevo software rápidamente; y la instalación de dependencias puede ser una pesadilla. Tenemos el propósito de que el menor número de personas posible sufra durante los procesos de instalación al integrar un nuevo software en nuestro flujo de trabajo.

    Como necesitábamos que cualquier persona de nuestro equipo pudiera realizar actualizaciones en la Demo de readmisiones para poder generar pacientes fácilmente, y no queríamos que todos tuvieran que instalar Gradle en sus equipos, nos apoyamos en Docker e introdujimos el software de Synthea dentro de una imagen de Docker, permitiendo que la imagen se ocupe de las dependencias ambientales subyacentes.

    Esto terminó funcionando muy bien para nuestro equipo, ya que nos dimos cuenta de que ser capaz de generar datos de pacientes sintéticos sobre la marcha es probablemente un caso de uso muy común al que se enfrentan nuestros compañeros Ingenieros de ventas, por lo que nuestro equipo quería compartirlo con la Comunidad de Desarrolladores.

    Cualquiera puede utilizar la siguiente línea de código para generar rápidamente 5 historiales médicos de pacientes sintéticos en formato FHIR, y dejar los pacientes resultantes en una carpeta de salida en el directorio donde está trabajando actualmente.

    docker run --rm -v $PWD/output:/output --name synthea-docker intersystemsdc/irisdemo-base-synthea:version-1.3.4 -p 5
      El código de esta imagen Docker tiene su propio repositorio en Github y se puede encontrar aquí para cualquier persona que quiera echar un vistazo, hacer cambios personalizados, o contribuir: https://github.com/intersystems-community/irisdemo-base-synthea

    Ahora estamos realizando actualizaciones para que el proyecto sea compatible con módulos personalizados, de modo que cualquiera que desee agregar una enfermedad a sus pacientes generados pueda hacerlo, si Synthea no la proporciona de forma predeterminada, y se incorporará automáticamente a su imagen.

    ¿Dónde se usa actualmente?

    El proceso actual de creación de la Demo de readmisiones utiliza la imagen irisdemo-base-synthea para generar 5 000 pacientes sintéticos sobre la marcha y cargarlos en nuestro repositorio de datos IRIS relacional, normalizado. Cualquier persona que esté interesada en verificar cómo analizar estos datos de pacientes generados de forma sintética (en formato FHIR), puede consultar la Demo de readmisiones creada recientemente con código abierto. La clase que hay que buscar es: IRISDemo.DataLake.Utils. a partir de la línea 613.

    La Demo de readmisiones se puede encontrar aquí: https://github.com/intersystems-community/irisdemo-demo-readmission

    1
    0 316
    Artículo Jose-Tomas Salvador · mayo 11, 2022 3m read

    Una de las formas más sencillas de configurar entornos de desarrollo repetibles es utilizar contenedores para ellos. Descubrí que cuando cambian rápidamente, era muy conveniente alojar una instancia de vscode dentro de mi contenedor de desarrollo. De este modo, he creado un script de contenedor sencillo que permite añadir en un contenedor IRIS un vscode basado en un navegador. Esto debería funcionar para la mayoría de los contenedores 2021.1 y posteriores. El repositorio de mi código se puede encontrar aquí

    El contenedor de InterSystems IRIS con vscode y pre-conectado

    CredValue
    User_SYSTEM
    PasswordSYS

    image

    Visión general

    Este proyecto crea un contenedor IRIS con una versión servidor (basada en web) de vscode disponible en el mismo contenedor IRIS. Esto ofrece:

    • Edición del código en el mismo contenedor
    • Pre-conexión a la instancia del contenedor de IRIS
    • Enlaces desde el Management Portal
    • Arranque automático del IDE con el contenedor

    Inicio rápido

    1. Descarga o git clone https://github.com/nickmitchko/Hosting-vscode-in-a-container.git
    2. En la raíz del proyecto, ejecuta docker build . -t vscode-irishealth-ml:latest --no-cache
    3. Ejecuta docker-compose up
      • No usas docker compose? Echa un vistazo aquí
    4. Ve al Management Portal
    5. Inicia sesión con el usuario y contraseña mostrada anteriormente
    6. Haz clic en el enlace a VSCODE en el panel Favoritos
    7. Cuando te pregunte, utiliza la misma contraseña en vscode para conectar con la instancia de IRIS.
    # New folder for project
    mkdir vscode-iris
    cd vscode-iris
    
    # Clone repo here
    git clone https://github.com/nickmitchko/Hosting-vscode-in-a-container.git .
    
    # Build image
    docker build . -t vscode-irishealth-ml:latest --no-cache
    
    # Only Run (A) or (B)
    #
    # (A) Run compose file
    docker-compose up
    # OR (B) if you want a daemon
    docker-compose up -d
    

    Añadiendo Persistencia

    Si quieres una instancia de IRIS persistente, descomenta las lineas 16 a 20 en el fichero docker-compose.yml. Esto monta un volumen de almacenamiento persistente al contenedor.

        volumes:
        - "./durable/:/durable/"
        environment:
        - ISC_DATA_DIRECTORY=/durable/iconfig
    

    Cambiando la imagen de base

    Esta imagen se construye sobre las imágenes zpm de la Comunidad de Desarrolladores de InterSystems (las puedes encontrar aquí). Estas imágenes incluyen el comando zpm que nos permite instalar fácilmente desde el repositorio, pero solo tiene una licencia "community" de 90 días.

    La etiqueta de imagen usada para builds es:

    FROM intersystemsdc/irishealth-ml-community:latest
    

    Si quieres cambiar la imagen, cambia la primera línea del archivo docker a tu etiqueta de imagen deseada (o una instancia de IRIS modificada o una oficialmente soportada). Por ejemplo:

    FROM containers.intersystems.com/intersystems/irishealth-community:2021.2.0.651.0
    

    Sin Docker-Compose

    Si no usas docker compose, aún puedes ejecutar el contenedor así:

    # After building the container
    # --after command is required
    docker run --name vscode -d \
        --publish 1972:1972 \
        --publish 52773:52773 \
        --publish 51773:51773 \
        --publish 53773:53773 \
        --publish 8080:8080 \
        --publish 8888:8888 \
        vscode-irishealth-ml:latest \
        --after "/bin/bash /install/boot.sh"
    
    1
    0 148
    Artículo Alberto Fuentes · abr 15, 2021 4m read

    Hola a todos! Comentamos hoy una entrada de Timothy Leavitt cuyo equipo (Application Services en InterSystems - encargado de desarrollar y mantener muchas de nuestras aplicaciones internas, y proporcionar herramientas y prácticas recomendadas a otras aplicaciones departamentales), durante el último año, se embarcó en un viaje hacia el desarrollo de interfaces de usuario basadas en Angular/REST, para las aplicaciones existentes construidas originalmente con CSP y/o Zen. Esto ha planteado un interesante reto, que os puede resultar familiar a muchos de vosotros: desarrollar nuevas APIs REST para los modelos de datos y la lógica empresarial existentes.

    Como parte de este proceso, creamos un nuevo framework para las APIs REST, que ha sido muy útil para nosotros mismos. Ya está disponible en Open Exchange en https://openexchange.intersystems.com/package/apps-rest. Creo que habrá más artículos sobre esto próximamente pero, mientras tanto, hay buenos tutoriales en la documentación del proyecto en GitHub (https://github.com/intersystems/apps-rest).

    Como introducción, aquí se encuentran algunos de nuestros objetivos e intenciones de diseño. Todavía no se han realizado todos, ¡pero vamos por buen camino!

    Desarrollo y despliegue rápidos

    Nuestro enfoque REST debería proporcionar el mismo inicio rápido para el desarrollo de aplicaciones que Zen, ya que resuelve problemas comunes y al mismo tiempo proporciona flexibilidad para los casos de uso específicos de las aplicaciones.

    • Exponer un nuevo recurso para el acceso a REST debería ser tan fácil como exponerlo a un DataModel de Zen.
    • La incorporación/modificación de recursos REST debería involucrar cambios en el nivel al que se accede.
    • La exposición de una clase persistente a través de REST debería llevarse a cabo por herencia y sobre-escrituras de métodos mínimos, pero también debería haber soporte para las funcionalidades equivalentes de programación manual (esto es similar a %ZEN.DataModel.Adaptor y a %ZEN.DataModel.ObjectDataModel).
    • Los patrones comunes de gestión/notificación de errores, serialización/deserialización, validación, etc., no deberían necesitar ser re-implementados para cada recurso en cada aplicación.
    • El soporte para las consultas, el filtrado y la clasificación de los datos de SQL, así como las funciones de búsqueda avanzada y la paginación, deberían estar incorporadas, en vez de que se vuelvan a implementar para cada aplicación.
    • Debería ser fácil desarrollar APIs REST para los métodos de clase y consultas de clase de la API/librería existente, así como a nivel de objeto (CRUD).

    Seguridad

    La seguridad es una decisión tomada en consecuencia en el momento de diseñar/implementar en vez de una idea de última hora.

    • Cuando las capacidades REST se obtienen por herencia de clases, el comportamiento por defecto debería ser NO proporcionar acceso al recurso, hasta que el desarrollador especifique activamente quién debe recibir acceso y bajo qué condiciones.
    • Las implementaciones estandarizadas de las funciones relacionadas con SQL minimizan la superficie potencial para los ataques de inyección SQL.
    • El diseño debe tener en cuenta el top 10 de la API OWASP (consulta: https://owasp.org/www-project-api-security)

    Mantenimiento

    La uniformidad del diseño de aplicaciones es una poderosa herramienta para el ecosistema de aplicaciones en una empresa.

    • En vez de acumular un conjunto de API REST de diversa índole e implementaciones programadas de forma manual, deberíamos tener APIs REST de aspecto similar en toda nuestra cartera. Esta uniformidad debería llevar a:
      • Técnicas de depuración comunes.
      • Técnicas de prueba comunes.
      • Técnicas comunes de interfaz de usuario para conectarse a las APIs REST.
      • Facilidad para desarrollar aplicaciones compuestas que accedan a varias APIs.
    • El conjunto de endpoints y el formato de las representaciones de objetos proporcionadas/aceptadas por medio de REST debería estar bien definido, de manera que podamos generar automáticamente la documentación de la API (por ejemplo, Swagger/OpenAPI) basada en estos endpoints.
    • Con base en la documentación estándar de la API, deberíamos ser capaces de generar partes del código del cliente (por ejemplo, clases de typescript correspondientes a nuestras representaciones REST) con la ayuda de herramientas de terceros/estándares de la industria.
    2
    0 214
    Artículo Ricardo Paiva · ene 29, 2021 8m read

    ¡Hola Desarroladores!

    IRIS External Table es un proyecto de código abierto de la comunidad de InterSystems, que permite utilizar archivos almacenados en el sistema de archivos local y almacenar objetos en la nube como AWS S3 y tablas SQL. IRIS External Table

    Se puede encontrar en GitHub https://github.com/intersystems-community/IRIS-ExternalTable Open Exchange https://openexchange.intersystems.com/package/IRIS-External-Table y está incluido en el administrador de paquetes InterSystems Package Manager (ZPM).

    Para instalar External Table desde GitHub, utilice:

    git clone https://github.com/antonum/IRIS-ExternalTable.git
    iris session iris
    USER>set sc = ##class(%SYSTEM.OBJ).LoadDir("<path-to>/IRIS-ExternalTable/src", "ck",,1)
    

    Para instalarlo con el ZPM Package Manager, utilice:

    USER>zpm "install external-table"
    

    Cómo trabajar con archivos locales

    Crearemos un archivo simple que tiene este aspecto:

    a1,b1
    a2,b2
    

    Abra su editor favorito y cree el archivo o utilice solo una línea de comandos en Linux/Mac:

    echo $'a1,b1\na2,b2' > /tmp/test.txt
    

    Cree una tabla SQL en IRIS para representar este archivo:

    create table test (col1 char(10),col2 char(10))
    

    Convierta la tabla para utilizar el almacenamiento externo:

    CALL EXT.ConvertToExternal(
        'test',
        '{
            "adapter":"EXT.LocalFile",
            "location":"/tmp/test.txt",
            "delimiter": ","
        }')
    

    Y finalmente, consulte la tabla:

    select * from test
    

    Si todo funciona según lo previsto, debería ver el resultado de la siguiente forma:

    col1    col2
    a1  b1
    a2  b2
    

    Ahora regrese al editor, modifique el contenido del archivo y ejecute nuevamente la consulta SQL. ¡¡¡Tarán!!! Está leyendo nuevos valores de su archivo local en SQL.

    col1    col2
    a1  b1
    a2  b99
    

    Cómo leer los datos desde S3

    En https://covid19-lake.s3.amazonaws.com/index.html puede acceder a los datos de la COVID que se actualizan constantemente, estos se almacenan por AWS en el lago de datos públicos.

    Intentaremos acceder a una de las fuentes de datos en este lago de datos: s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states

    Si tiene instalada la herramienta de línea de comandos para AWS, puede repetir los siguientes pasos. Si no es así, vaya directamente a la parte de SQL. No es necesario que tenga ningún componente específico de AWS instalado en su equipo para continuar con la parte de SQL.

    $ aws s3 ls s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/
    2020-12-04 17:19:10     510572 us-states.csv
    
    $ aws s3 cp s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv .
    download: s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv to ./us-states.csv
    
    $ head us-states.csv 
    date,state,fips,cases,deaths
    2020-01-21,Washington,53,1,0
    2020-01-22,Washington,53,1,0
    2020-01-23,Washington,53,1,0
    2020-01-24,Illinois,17,1,0
    2020-01-24,Washington,53,1,0
    2020-01-25,California,06,1,0
    2020-01-25,Illinois,17,1,0
    2020-01-25,Washington,53,1,0
    2020-01-26,Arizona,04,1,0
    

    Por lo tanto, tenemos un archivo con una estructura bastante simple y cinco campos delimitados.

    Para mostrar esta carpeta S3 como en External Table, primero necesitamos crear una tabla “regular” con la estructura deseada:

    -- create external table
    create table covid_by_state (
        "date" DATE, 
        "state" VARCHAR(20),
        fips INT,
        cases INT,
        deaths INT
    )
    

    Tenga en cuenta que algunos campos de datos como “Date” son palabras reservadas en el SQL de IRIS y deben escribirse entre comillas dobles. Entonces, necesitamos convertir esta tabla “regular” en la tabla “externa”, basada en el bucket AWS S3 y con el tipo CSV.

     -- convert table to external storage
    call EXT.ConvertToExternal(
        'covid_by_state',
        '{
        "adapter":"EXT.AWSS3",
        "location":"s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/",
        "type": "csv",
        "delimiter": ",",
        "skipHeaders": 1
        }' 
    )
    

    Si observa detenidamente, en EXT.ExternalTable los argumentos de los procedimientos son el nombre de la tabla y luego la cadena JSON, además contiene varios parámetros como la ubicación para buscar archivos, el adaptador para utilizarlos, un delimitador, etc. Además, External Table de AWS S3 es compatible con el almacenamiento de Azure BLOB, Google Cloud Buckets y el sistema de archivos local. El repositorio de GitHub contiene referencias para la sintaxis y opciones que son compatibles con todos los formatos.

    Y finalmente, consulte la tabla:

    -- query the table
    select top 10 * from covid_by_state order by "date" desc
    
    [SQL]USER>>select top 10 * from covid_by_state order by "date" desc
    2.  select top 10 * from covid_by_state order by "date" desc
    
    date    state   fips    cases   deaths
    2020-12-06  Alabama 01  269877  3889
    2020-12-06  Alaska  02  36847   136
    2020-12-06  Arizona 04  364276  6950
    2020-12-06  Arkansas    05  170924  2660
    2020-12-06  California  06  1371940 19937
    2020-12-06  Colorado    08  262460  3437
    2020-12-06  Connecticut 09  127715  5146
    2020-12-06  Delaware    10  39912   793
    2020-12-06  District of Columbia    11  23136   697
    2020-12-06  Florida 12  1058066 19176
    

    Es comprensible que se necesite más tiempo para consultar los datos de la tabla remota, que para consultar la tabla “nativa de IRIS” o la tabla basada en el global, pero esta se almacena y actualiza completamente en la nube, y en segundo plano se extrae a IRIS.

    Exploremos un par de funciones adicionales de External Table.

    %PATH y las tablas basadas en varios archivos

    La carpeta de nuestro ejemplo, que se encuentra en el bucket, contiene solo un archivo. Lo más común es que tenga varios archivos de la misma estructura, donde el nombre del archivo identifique tanto al registro de la hora como al identificador de algún otro atributo que queramos utilizar en nuestras consultas.

    El campo %PATH se agrega automáticamente a cada tabla externa y contiene la ruta completa hacia el archivo de donde se recuperó la fila.

    select top 5 %PATH,* from covid_by_state
    
    %PATH   date    state   fips    cases   deaths
    s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-21  Washington  53  1   0
    s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-22  Washington  53  1   0
    s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-23  Washington  53  1   0
    s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-24  Illinois    17  1   0
    s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-24  Washington  53  1   0
    

    Puede utilizar el campo %PATH en sus consultas SQL como cualquier otro campo.

    De datos ETL a “tablas regulares”

    Si su tarea es cargar datos de S3 en una tabla IRIS, puede utilizar External Table como una herramienta ETL. Simplemente haga lo siguiente:

    INSERT INTO internal_table SELECT * FROM external_table
    

    En nuestro caso, si queremos copiar los datos COVID de S3 a la tabla local:

    --create local table
    create table covid_by_state_local (
        "date" DATE, 
        "state" VARCHAR(100),
        fips INT,
        cases INT,
        deaths INT
    )
    --ETL from External to Local table
    INSERT INTO covid_by_state_local SELECT TO_DATE("date",'YYYY-MM-DD'),state,fips,cases,deaths FROM covid_by_state
    

    UNIÓN entre IRIS, tablas nativas y externas. Consultas federadas

    External Table es una tabla SQL. Se puede unir con otras tablas, utilizarse en subconsultas y sistemas de archivos tipo UNION. Incluso puede combinar la tabla “Regular” de IRIS y dos o más tablas externas que provengan de diferentes fuentes en la misma consulta SQL.

    Intente crear una tabla regular, por ejemplo, haga coincidir los nombres de los estados con sus códigos como en el caso de Washington y WA. Y únalos con nuestra tabla basada en S3.

    create table state_codes (name varchar(100), code char(2))
    insert into state_codes values ('Washington','WA')
    insert into state_codes values ('Illinois','IL')
    
    select top 10 "date", state, code, cases from covid_by_state join state_codes on state=name
    

    Cambie “join” por “left join” para incluir aquellas filas donde el código del estado no esté definido. Como puede ver, el resultado es una combinación de datos provenientes de S3 y su tabla nativa de IRIS.

    Acceso seguro a la información

    El lago de datos Covid en AWS es público. Cualquier persona puede leer los datos que provengan de esta fuente sin la necesidad de tener alguna autenticación o autorización. En la vida real seguramente quiere acceder a sus datos de una forma segura, donde se evite que extraños echen un vistazo a sus archivos. Los detalles completos sobre AWS Identity y Access Management (IAM) están fuera del alcance de este artículo. Pero lo mínimo que debe saber es que necesita por lo menos la clave de acceso a la cuenta y la información confidencial de AWS para acceder a los datos privados de su cuenta. https:

    AWS utiliza la autenticación de claves/información confidencial de la cuenta para firmar las solicitudes. https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys

    Si está ejecutando IRIS External Table en una instancia de EC2, la forma recomendada de lidiar con la autenticación es utilizando las funciones que se encuentran en la instancia de EC2 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html. De este modo, IRIS External Table podría utilizar los permisos de esa función. No se requiere ninguna configuración adicional.

    En una instancia local o que no sea de EC2 es necesario especificar AWS_ACCESS_KEY_ID y AWS_SECRET_ACCESS_KEY, ya sea con la ayuda de variables de entorno o mediante la instalación y configuración del cliente CLI de AWS.

    export AWS_ACCESS_KEY_ID=AKIAEXAMPLEKEY
    export AWS_SECRET_ACCESS_KEY=111222333abcdefghigklmnopqrst
    

    Asegúrese de que la variable de entorno sea visible dentro del proceso de IRIS. Puede verificarlo al ejecutar:

    USER>write $system.Util.GetEnviron("AWS_ACCESS_KEY_ID")
    

    Esto debería emitir el valor de la clave.

    O instale el CLI de AWS, mediante instrucciones que se encuentran aquí: https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html y ejecutar:

    aws configure
    

    Entonces External Table podrá leer las credenciales desde los archivos de configuración para el CLI de AWS. Posiblemente su shell interactivo y el proceso de IRIS estén ejecutándose en cuentas diferentes, asegúrese de ejecutar aws configure con la misma cuenta que su proceso de IRIS.

    1
    0 147
    Artículo Yaron Munz · sep 23, 2022 5m read

    Resumen

    Empezamos a usar Azure Service Bus (ASB) como solución de mensajería empresarial hace tres años. La hemos usado para publicar y consumir datos entre muchas aplicaciones de la organización. Como el flujo de datos es complejo, y normalmente se necesitan los datos de una aplicación en muchas otras aplicaciones, el modelo publicador -> múltiples subscriptores resultó muy adecuado. El uso de ASB en la organización es de docenas de millones de mensajes por día, mientras que la plataforma IRIS tiene unos 2-3 millones de mensajes/día.

    El problema con ASB

    Cuando empezamos con la integración de ASB, encontramos que el protocolo AMQP no tiene la configuración predeterminada para la implementación de IRIS, por lo que estuvimos buscando una solución alternativa para poder comunicar con ASB.

    1
    1 160
    Artículo Henry Pereira · sep 26, 2021 8m read

    https://media.giphy.com/media/Nxu57gIbNuYOQ/giphy.gif

    Calma, calma, no estoy promoviendo una guerra contra las máquinas como en las películas de ciencia ficción, para evitar la dominación mundial de Ultron o Skynet. Todavía no, todavía no 🤔

    Os invito a retar a las máquinas a través de la creación de un juego muy sencillo usando ObjectScript con Python embebido.

    Tengo que deciros que me emocioné mucho con la función de Python integrado en InterSystems IRIS. Es increíble el montón de posibilidades que se abren para crear aplicaciones fantásticas.

    Vamos a construir un juego "tres en raya". Las reglas son bastante sencillas y creo que todo el mundo sabe jugar.

    Es lo que me salvó del tedio en mi infancia, durante los largos viajes en coche con la familia antes de que los niños tuvieran teléfonos móviles o tabletas. ¡Nada como retar a mis hermanos a jugar unas partidas en el cristal borroso!

    Así que... ¡abrochaos el cinturón y vámonos!

    Normas

    Como he comentado, las reglas son bastante simples:

    • solo 2 jugadores por set
    • se juega por turnos en una cuadrícula de 3x3
    • el jugador humano siempre será la letra X y la computadora la letra O
    • los jugadores solo podrán poner las letras en los espacios vacíos
    • el primero en completar una secuencia de 3 letras iguales en horizontal, o en vertical o en diagonal, es el ganador
    • cuando se ocupen los 9 espacios, será un empate y el final de la partida

    https://media4.giphy.com/media/3oriNKQe0D6uQVjcIM/giphy.gif?cid=790b761123702fb0ddd8e14b01746685cc0059bac0bc66e9&rid=giphy.gif&ct=g

    Todo el mecanismo y las reglas lo escribiremos en ObjectScript, el mecanismo del jugador de la máquina se escribirá en Python.

    Vamos a trabajar

    Controlaremos el tablero en un global, en el que cada fila estará en un nodo y cada columna en una pieza.

    Nuestro primer método es iniciar el tablero, para que sea fácil iniciaré el global ya con los nodos (filas A, B y C) y con las 3 piezas:

    /// Iniciar un juego nuevo
    ClassMethod NewGame() As %Status
    {
      Set sc = $$$OK
      Kill ^TicTacToe
      Set ^TicTacToe("A") = "^^"
      Set ^TicTacToe("B") = "^^"
      Set ^TicTacToe("C") = "^^"
      Return sc
    }
    

    en este momento crearemos un método para añadir las letras en los espacios vacíos, para esto cada jugador dará la ubicación del espacio en el tablero.

    Cada fila una letra y cada columna un número, para poner la X en el medio, por ejemplo, pasamos B2 y la letra X al método.

    ClassMethod MakeMove(move As %String, player As %String) As %Boolean
    {
      Set $Piece(^TicTacToe($Extract(move,1,1)),"^",$Extract(move,2,2)) = player
    }
    

    Vamos a comprobar si la coordinación es válida, la forma más simple que veo es usando una expresión regular:

    ClassMethod CheckMoveIsValid(move As %String) As %Boolean
    {
      Set regex = ##class(%Regex.Matcher).%New("(A|B|C){1}[0-9]{1}")
      Set regex.Text = $ZCONVERT(move,"U")
      Return regex.Locate()
    }
    

    Necesitamos garantizar que el espacio seleccionado está vacío.

    ClassMethod IsSpaceFree(move As %String) As %Boolean
    {
      Quit ($Piece(^TicTacToe($Extract(move,1,1)),"^",$Extract(move,2,2)) = "")
    }
    

    ¡Muy bien!

    Ahora comprobamos si algún jugador ganó el set o si el juego ya está terminado, para esto creemos el método CheckGameResult.

    Primero verificamos si hubo algún ganador completando por la horizontal, usaremos una lista con las filas y un simple $ Find resuelve

        Set lines = $ListBuild("A","B","C")
        // Check Horizontal
        For i = 1:1:3 {
          Set line = ^TicTacToe($List(lines, i))
          If (($Find(line,"X^X^X")>0)||($Find(line,"O^O^O")>0)) {
            Return $Piece(^TicTacToe($List(lines, i)),"^", 1)_" won"
          }
        }
    

    Con otro For comprobamos la vertical

    For j = 1:1:3 {
          If (($Piece(^TicTacToe($List(lines, 1)),"^",j)'="") &&
            ($Piece(^TicTacToe($List(lines, 1)),"^",j)=$Piece(^TicTacToe($List(lines, 2)),"^",j)) &&
            ($Piece(^TicTacToe($List(lines, 2)),"^",j)=$Piece(^TicTacToe($List(lines, 3)),"^",j))) {
            Return $Piece(^TicTacToe($List(lines, 1)),"^",j)_" won"
          }
        }
    

    para comprobar la diagonal:

        If (($Piece(^TicTacToe($List(lines, 2)),"^",2)'="") &&
          (
            (($Piece(^TicTacToe($List(lines, 1)),"^",1)=$Piece(^TicTacToe($List(lines, 2)),"^",2)) &&
              ($Piece(^TicTacToe($List(lines, 2)),"^",2)=$Piece(^TicTacToe($List(lines, 3)),"^",3)))||
            (($Piece(^TicTacToe($List(lines, 1)),"^",3)=$Piece(^TicTacToe($List(lines, 2)),"^",2)) &&
            ($Piece(^TicTacToe($List(lines, 2)),"^",2)=$Piece(^TicTacToe($List(lines, 3)),"^",1)))
          )) {
          Return ..WhoWon($Piece(^TicTacToe($List(lines, 2)),"^",2))
        }
    

    por fin, comprobamos si hubo un empate

        Set gameStatus = ""
        For i = 1:1:3 {
          For j = 1:1:3 {
            Set:($Piece(^TicTacToe($List(lines, i)),"^",j)="") gameStatus = "Not Done"
          }
        }
        Set:(gameStatus = "") gameStatus = "Draw"
    

    ¡Genial!

    Es hora de construir la máquina

    Vamos a crear a nuestro oponente, necesitamos crear un algoritmo capaz de calcular todos los movimientos disponibles y usar una métrica para saber cuál es el mejor movimiento.

    Lo ideal es utilizar un algoritmo de decisión llamado MiniMax (Wikipedia: MiniMax)

    https://media3.giphy.com/media/WhTC5v5qQP4yAUvGKz/giphy.gif?cid=ecf05e47cx92yiew8vsig62tjq738xf7hfde0a2ygyfdl0xt&rid=giphy.gif&ct=g

    El algoritmo MiniMax es una regla de decisión utilizada en teoría de juegos, teoría de decisiones e inteligencia artificial.

    Básicamente, necesitamos saber cómo jugar asumiendo cuáles serán los posibles movimientos del oponente y coger el mejor escenario posible.

    En detalle, tomamos la escena actual y comprobamos de forma recurrente el resultado del movimiento de cada jugador. En caso de que la máquina gane el juego, puntuamos con +1, en caso de que pierda, puntuamos con -1 y con 0 si empata.

    Si no es el final del juego, abrimos otro árbol con el estado actual del juego. Después de eso, encontramos la jugada con el valor máximo para la máquina y el mínimo para el oponente.

    Mira el siguiente gráfico - hay 3 movimientos disponibles: B2, C1 y C3.

    Al elegir C1 o C3, el oponente tiene la oportunidad de ganar en el siguiente turno, pero si elige B2 no importa el movimiento que elija el oponente, la máquina gana la partida.

    minimaxp

    Es como tener la gema del tiempo en nuestras manos e intentar encontrar la mejor línea de tiempo.

    https://pa1.narvii.com/7398/463c11d54d8203aac94cda3c906c40efccf5fd77r1-460-184_hq.gif

    Convirtiendo a Python

    ClassMethod ComputerMove() As %String [ Language = python ]
    {
      import iris
      from math import inf as infinity
      computerLetter = "O"
      playerLetter = "X"
    
      def isBoardFull(board):
        for i in range(0, 8):
          if isSpaceFree(board, i):
            return False
        return True
    
      def makeMove(board, letter, move):
        board[move] = letter
    
      def isWinner(brd, let):
        # check horizontals
        if ((brd[0] == brd[1] == brd[2] == let) or \
          (brd[3] == brd[4] == brd[5] == let) or \
          (brd[6] == brd[7] == brd[8] == let)):
            return True
        # check verticals
        if ((brd[0] == brd[3] == brd[6] == let) or \
            (brd[1] == brd[4] == brd[7] == let) or \
            (brd[2] == brd[5] == brd[8] == let)):
            return True
        # check diagonals
        if ((brd[0] == brd[4] == brd[8] == let) or \
            (brd[2] == brd[4] == brd[6] == let)):
            return True
        return False
    
      def isSpaceFree(board, move):
        #Retorna true se o espaco solicitado esta livre no quadro
        if(board[move] == ''):
          return True
        else:
          return False
    
      def copyGameState(board):
        dupeBoard = []
        for i in board:
          dupeBoard.append(i)
        return dupeBoard
    
      def getBestMove(state, player):
        done = "Done" if isBoardFull(state) else ""
        if done == "Done" and isWinner(state, computerLetter): # If Computer won
          return 1
        elif done == "Done" and isWinner(state, playerLetter): # If Human won
          return -1
        elif done == "Done":    # Draw condition
          return 0
    
        # Minimax Algorithm
        moves = []
        empty_cells = []
        for i in range(0,9):
          if state[i] == '':
            empty_cells.append(i)
    
        for empty_cell in empty_cells:
          move = {}
          move['index'] = empty_cell
          new_state = copyGameState(state)
          makeMove(new_state, player, empty_cell)
    
          if player == computerLetter:
              result = getBestMove(new_state, playerLetter)
              move['score'] = result
          else:
              result = getBestMove(new_state, computerLetter)
              move['score'] = result
    
          moves.append(move)
    
        # Find best move
        best_move = None
        if player == computerLetter:
            best = -infinity
            for move in moves:
                if move['score'] > best:
                    best = move['score']
                    best_move = move['index']
        else:
            best = infinity
            for move in moves:
                if move['score'] < best:
                    best = move['score']
                    best_move = move['index']
    
        return best_move
    
      lines = ['A', 'B', 'C']
      game = []
      current_game_state = iris.gref("^TicTacToe")
    
      for line in lines:
        for cell in current_game_state[line].split("^"):
          game.append(cell)
    
      cellNumber = getBestMove(game, computerLetter)
      next_move = lines[int(cellNumber/3)]+ str(int(cellNumber%3)+1)
      return next_move
    }
    

    Primero, convierto el global en una matriz simple, ignorando columnas y filas, dejándolo plano para facilitar.

    En cada movimiento analizado llamamos al método copyGameState que, como su nombre indica, copia el estado del juego en ese momento, donde aplicamos el MiniMax.

    El método getBestMove que se llamará repetidamente hasta que finalice el juego encontrando un ganador o un empate.

    Primero se mapean los espacios vacíos y verificamos el resultado de cada movimiento, cambiando entre los jugadores.

    Los resultados se almacenan en move ['puntuación'] para, después de comprobar todas las posibilidades, encontrar el mejor movimiento.

    ¡Espero que os haya divertido! Es posible mejorar la inteligencia usando algoritmos como Alpha-Beta Pruning (Wikipedia: AlphaBeta Pruning) o redes neuronales. ¡Solo tened cuidado de no darle vida a Skynet!

    https://media4.giphy.com/media/mBpthYTk5rfbZvdtIy/giphy.gif?cid=790b761181bf3c36d85a50b84ced8ac3c6c937987b7b0516&rid=giphy.gif&ct=g

    No dudeis en dejar cualquier comentario o pregunta.

    ¡Esto es todo, amigos!

    Código fuente completo: InterSystems Iris version 2021.1.0PYTHON

    1
    0 866
    Artículo Joel Espinoza · oct 28, 2019 5m read

    ¡Hola a todos!

    Hoy instalaremos Jupyter Notebook y vamos a conectarlo con Apache Spark e InterSystems IRIS.

    Nota: Los siguientes procedimientos los hice en Ubuntu 18.04 y Python 3.6.5.

    Introducción

    Si estás buscando un bloc de notas que sea reconocido, difundido ampliamente y muy popular entre los usuarios de Python, en lugar de utilizar Apache Zeppelin, deberías elegir Jupyter notebook. Jupyter notebook es una excelente y muy poderosa herramienta para la "ciencia de datos", que cuenta con una comunidad muy grande, además de muchas funciones y software adicional. Jupyter notebook permite crear y compartir documentos que contienen código en tiempo real, ecuaciones, visualizaciones y texto narrativo. Sus aplicaciones incluyen la limpieza y transformación de los datos, simulaciones numéricas, modelamiento estadístico, visualización de datos, machine learning y muchas funciones más. Y lo más importante, existe una gran comunidad que  ayuda a resolver los problemas que surjan.

    1
    0 595
    Artículo Alberto Fuentes · ago 25, 2022 4m read

    Hace varios años, estaba enseñando los conocimientos básicos de nuestro framework %UnitTest durante la clase de Fundamentos de Caché (ahora llamada Developing Using InterSystems Objects and SQL). Un alumno preguntó si era posible recoger estadísticas de rendimiento mientras se ejecutan pruebas unitarias. Unas semanas más tarde, añadí un código adicional a los ejemplos de %UnitTest para responder a esa pregunta. Ahora lo comparto con la Comunidad.

    1
    0 107
    Artículo Alberto Fuentes · ago 4, 2022 11m read

    Al igual que los servidores hardware, los servidores virtuales en nubes públicas y privadas pueden generar cuellos de botella en los recursos, según aumentan las cargas de trabajo. Si utilizas y administras instancias de InterSystems IRIS implementadas en nubes públicas o privadas, es posible que te hayas encontrado la situación en la que para solucionar problemas de rendimiento o de otro tipo se requiere aumentar la capacidad del servidor de una instancia (es decir, escalar verticalmente).

    Un motivo frecuente es la memoria insuficiente. Como se describe en la Administración de la memoria y escalamiento de InterSystems IRIS que se encuentra en la Guía de escalabilidad, proporcionar suficiente memoria para todas las estructuras que se ejecutan en el servidor de una instancia de InterSystems IRIS en todas las circunstancias normales de funcionamiento, es un factor crítico tanto para el rendimiento como para la disponibilidad. En un escenario común, conforme aumenta la carga de trabajo en una instancia de InterSystems IRIS, su conjunto de trabajo se vuelve demasiado grande para ser contenido por la memoria caché reservada para las estructuras de la base de datos. Esto lo obliga a que algunas consultas tengan que ir a disco, lo que aumenta significativamente el número de lecturas requeridas del disco y crea un problema importante de rendimiento. Aumentar el tamaño de esta memoria caché resuelve ese problema, pero si al hacerlo no queda suficiente memoria para otros propósitos, también habrá que aumentar la memoria física total del servidor para evitar que el cuello de botella se desplace hacia otra parte del sistema.

    Afortunadamente, escalar un servidor virtual generalmente es mucho más sencillo que escalar uno hardware. En esta publicación se analizan las dos etapas del proceso:

    • Cómo escalar los recursos del servidor virtual 

    Se puede cambiar la especificación de recursos de un servidor virtual en AWS, GCP y Azure, utilizando la línea de comandos, la API o el portal de la plataforma. VMWare vSphere permite modificar fácilmente varios parámetros de los recursos para una máquina virtual mediante su interfaz de cliente vSphere.

    • Cómo reconfigurar InterSystems IRIS para aprovechar los recursos escalados

    Hay varias maneras de reconfigurar InterSystems IRIS para aprovechar los recursos del servidor escalado. Este documento describe el uso de la función Combinar la configuración, que combina nuevos valores de los parámetros, especificados en un archivo combinado, en el CPF de una instancia. Combinar la configuración es un método sencillo y eficaz porque permite ocuparse únicamente de la configuración que se quiere modificar, hacer varios cambios en la configuración de una instancia con una sola operación y realizar fácilmente el mismo conjunto de cambios en varias instancias.

    Los procedimientos descritos aquí son manuales, pero en producción muy probablemente serían automatizados, por ejemplo usando un script que aplicaría un archivo combinado específico en una ubicación accesible para una lista de instancias.

    Cómo escalar los recursos del host virtual

    Las plataformas publicas en la nube ofrecen una variedad de plantillas de recursos para elegir, que especifican el CPU, la memoria, las interfaces de red y otros recursos para hosts virtuales (el almacenamiento se suministra y se dimensiona por separado). Para cambiar el tamaño de un servidor, hay que cambiar la plantilla seleccionada cuando se creó el host a una permita especificar los recursos que necesitas aumentar. En Amazon Web Services, la plantilla de recursos se denomina un tipo de instancia, por ejemplo, el tipo de instancia t3.large especifica 2 CPUs y 8 GB de memoria. En la plataforma Google Cloud es un tipo de máquina, como la e2-standard-2 (que también incluye 2 CPUs y 8 GB), y en Microsoft Azure es un tamaño (el Standard_B2ms requiere igualmente 2 CPUs y 8 GB). Al redefinir el tipo de instancia, tipo de máquina o tamaño del servidor de una nube pública existente, se pueden escalar las especificaciones de sus recursos. En una nube privada de VMware vSphere, se puede utilizar la interfaz de cliente vSphere en la consola de administración vCenter Server para modificar directamente una o más configuraciones de los recursos individuales de una máquina virtual existente. (También se pueden escalar simultáneamente grupos de servidores en cada plataforma).

    En las siguientes secciones se ofrecen breves ejemplos sobre cómo redimensionar servidores virtuales individuales en distintas plataformas, con enlaces a la documentación para todos los métodos disponibles. Ten en cuenta que estos métodos (API, interfaces de línea de comandos e interfaces del portal) se proporcionan y mantienen gracias a los proveedores en la nube, y los ejemplos que aquí se incluyen son con propósitos informativos, para ilustrar con qué facilidad se puede adaptar InterSystems IRIS para aprovechar el aumento en los recursos. 

    AWS

    Para modificar el tipo de instancia de un servidor AWS (denominada instance, no debe confundirse con una instancia de InterSystems IRIS) se puede utilizar el comando CLI modify-instance-attribute, como se muestra en el siguiente ejemplo:

    $ aws ec2 describe-instances --instance-ids i-01519f663af48a55e
    {
       "Instances": [
            {
                "AmiLaunchIndex": 0,
                "ImageId": "ami-0abcdef1234567890,
                "InstanceId": "i-1234567890abcdef0,
                "InstanceType": "m5n.large",
                ...
    $ aws ec2 stop-instances --instance-ids i-01519f663af48a55e
    {
        "StoppingInstances": [
            {
                "InstanceId": "i-1234567890abcdef0",
                ...
    $ aws ec2 describe-instances --instance-ids i-01519f663af48a55e
    {
       "Instances": [
            {
                ...
                "State": {
                "Code": 80, 
                "Name": "stopped"
                }
                ...
    $ aws ec2 modify-instance-attribute --instance-ids i-01519f663af48a55e \
          --instance-type "{\"Value\": \"m5n.xlarge\"}"
    $ aws ec2 start-instances --instance-ids i-01519f663af48a55e
    {
        "StartingInstances": [
            {
                "InstanceId": "i-1234567890abcdef0",
                "CurrentState": {
                    "Code": 0,
                    "Name": "pending"
                },
                "PreviousState": {
                    "Code": 80,
                    "Name": "stopped"
                ...
    $ aws ec2 describe-instances --instance-ids i-01519f663af48a55e
    {
       "Instances": [
            {
                "AmiLaunchIndex": 0,
                "ImageId": "ami-0abcdef1234567890,
                "InstanceId": "i-1234567890abcdef0,
                "InstanceType": "m5n.xlarge",
                ...

    También se puede hacer este cambio mediante la llamada a la API de AWS ModifyInstanceAttribute o con la consola AWS EC2

    GCP

    Para modificar el tipo de máquina de un servidor GCP (también conocida como una instance), se puede usar el comando gcloud CLI para detener, modificar y reiniciar la instancia. Por ejemplo, se podrían usar los siguientes comandos para modificar el tipo de máquina de una instancia llamada scalingTest por n1-highmem-96:

    $ gcloud compute instances stop scalingTest
    $ gcloud compute instances set-machine-type scalingTest --machine-type n1-highmem-32
    $ gcloud compute instances start scalingTest

    También se puede hacer este cambio usando la Google Cloud Console o la API de la GCP.

    Azure

    Cuando se utiliza Azure CLI para modificar el tamaño de una máquina virtual con Linux, se puede ver una lista de los tamaños disponibles en el clúster de hardware donde se aloja la máquina virtual usando el comando list-vm-resize-options, por ejemplo:

    az vm list-vm-resize-options --resource-group testingGroup --name scalingTest --output table

    Se puede utilizar el comando resize para cambiar el tamaño de la máquina virtual a una de las opciones en la lista, como se muestra en el ejemplo. Este comando reinicia la máquina virtual automáticamente.

    az vm resize --resource-group testingGroup --name scalingTest --size Standard_E32d_v4

    Si el tamaño al que quieres cambiar la máquina virtual no está disponible, puede anular la asignación de la máquina virtual, que puede ser redimensionada a cualquier tamaño que esté soportado por la región y reiniciarse. Los comandos relacionados se muestran a continuación:

    az vm deallocate --resource-group testingGroup --name scalingTest
    az vm resize --resource-group testingGroup --name scalingTest --size Standard_M128s
    az vm start --resource-group testingGroup --name scalingTest

    Se puede cambiar el tamaño de una máquina virtual de Windows en Azure utilizando el portal de Azure o los comandos de Powershell.

    vSphere

    Para cambiar el tamaño de una máquina virtual de VMware vSphere, hay que hacer lo siguiente:

    1. Abrir el cliente vSphere o el cliente web y mostrar el inventario de la máquina virtual.
    2. Haga clic con el botón derecho en la máquina virtual que quieres modificar y seleccionar Edit Settings.
    3. En la etiqueta Virtual Hardware
      • Expande Memory y cambia la cantidad de RAM configurada para la máquina virtual.
      • Expande CPU y cambia el número de núcleos y, de manera opcional, el número de núcleos por socket.
      • Realiza cualquier otro cambio que quieras en los recursos de hardware asignados a la máquina virtual.

    Cómo reconfigurar InterSystems IRIS para aprovechar los recursos escalados

    Cuando hayas escalado el servidor, el siguiente paso es reconfigurar InterSystems IRIS para aprovechar el aumento de recursos cambiando uno o más parámetros en el archivo de parámetros de configuración de la instancia (CPF). Por ejemplo, para continuar con el escenario mencionado al principio de esta publicación, ahora que has aumentado los recursos de memoria del servidor, querrás aprovecharte de esto aumentando el tamaño de la caché de la base de datos de la instancia de InterSystems IRIS (que se realiza cambiando el valor del parámetro globals) para que pueda mantener más datos en la memoria.

    Una manera sencilla de realizar este tipo de cambios, y con mucho la forma más fácil y repetible para realizar varios cambios en la configuración de una instancia en una sola operación o de realizar los mismos cambios en varias instancias, es utilizar la función Combinar la configuración, que está disponible en los sistemas UNIX® y Linux. Como se describe en Cómo usar la función Combinar la configuración para implementar instancias personalizadas de InterSystems IRIS en Cómo ejecutar productos de InterSystems en contenedores y en Cómo usar la función Combinar la configuración en la Referencia del Archivo de Configuración de Parámetros, la función Combinar la configuración permite especificar un archivo de combinación que contenga la configuración que quieres combinar en el CPF de una instancia, inmediatamente antes de un reinicio.  (En la versión 2021.1 podrás hacer esto en una instancia que está en ejecución sin reiniciarla). Esto no solo es más conveniente que editar directamente el CPF de una instancia, sino que es altamente repetible en varias instancias, y es compatible con una gestión de cambios confiables, ya que permite mantener un historial preciso de los cambios simplemente adaptando la configuración de los archivos combinados a los que los aplique.

    Para realizar la función Combinar la configuración, hay que hacer lo siguiente:

    1. Crear el archivo combinado con los parámetros que quieres modificar.
    2. Colocar el archivo combinado en una ubicación accesible a la instancia. Si la instancia que estás modificando se encuentra en un contenedor (el cual es probable que esté en un host de la nube), puedes preparar el archivo en el directorio %SYS duradero de la instancia (consulta %SYS duradero para datos persistentes de la instancia en Cómo ejecutar productos de InterSystems en contenedores).
    3. Especifique la ubicación del archivo combinado utilizando la variable de entorno ISC_CPF_MERGE_FILE antes de reiniciar la instancia.

    Por ejemplo, siguiendo con el caso de la caché de la base de datos que necesita una actualización, supongamos que queremos aumentar a 100 GB el tamaño de la caché de la base de datos de una instancia en un contenedor. La configuración, en la sección [config] del CPF, sería globals=102400, que establece la caché de la base de datos para bloques de 8 kilobytes en 102,400 MB, o 100 GB. (Como se explica en la descripción de los globals en la Referencia del Archivo de Configuración de Parámetros, el parámetro establece el tamaño de la caché para varios tamaños de bloque; sin embargo, si solo se proporciona un valor, se aplica al tamaño de bloque de 8 kilobytes, y se asume **** [zero] para los otros tamaños; globals=102400 es, por lo tanto, el equivalente a globals=0,0,102400,0,0,0).

    Para realizar este cambio, se puede realizar lo siguiente en el host de la nube:

    1. Crear una configuración en el archivo combinado, denominado por ejemplo mergefile2021.06.30.cpf, que contenga estas líneas:

    [config]
    globals=102400
    

    2. Colocar el archivo combinado en el directorio %SYS duradero que se encuentra en el sistema de archivos del host, el cual si se instaló el volumen externo /data como /external en el contenedor y se usó la variable ISC_DATA_DIRECTORY para especificar /external/iris_durable como el directorio %SYS duradero para la instancia, sería /data/iris_durable.

    3. Utilizar el comando docker exec en la línea de comandos del host para especificar la variable y reiniciar la instancia con el comando iris si el contenedor de la instancia se llama iris y la instancia se llama IRIS, por ejemplo, el comando tendrá el siguiente aspecto:

    docker exec iris ISC_CPF_MERGE_FILE=/data/iris_durable/mergefile2021.06.30.cpf 
    iris stop IRIS restart
    
    1. Cuando la instancia se reinicie, se podrá confirmar la nueva configuración de globals con este comando:
    docker exec iris grep globals /data/iris_durable/iris.cpf
    
    1
    0 130
    Artículo Alberto Fuentes · abr 22, 2022 4m read

    Durante una actualización a una versión principal (major) es aconsejable recompilar las clases y rutinas de todos tus namespaces (ver Tareas tras la instalación de una versión major).

    do $system.OBJ.CompileAllNamespaces("u")
    do ##Class(%Routine).CompileAllNamespaces()

    Para automatizar esta tarea de administración y mantener un registro de cualquier error, os muestro un ejemplo de una clase para importar y compilar en el namespace USER, que puedes usar después de cada actualización: admin.utils.cls

    1
    0 183
    Artículo Alberto Fuentes · mayo 25, 2021 12m read

    En este artículo, me gustaría hablar sobre el enfoque spec-first para el desarrollo de una API REST.

    Mientras que el desarrollo tradicional code-first de una API REST es así:

    • Escribir el código
    • Habilitarlo en REST
    • Documentarlo (como una API REST)

    Spec-first sigue los mismos pasos, pero a la inversa. Comenzamos con una especificación, — que también actúa como documentación — , generamos el código base de la aplicación REST a partir de ella, y finalmente escribimos la lógica de negocio concreta que nos haga falta.

    Esto ofrece varias ventajas:

    • Siempre se dispone de documentación relevante y útil para desarrolladores externos o de frontend que quieran utilizar tu API REST
    • La especificación creada en OAS (Swagger) se puede importar a una variedad de herramientas que permiten la edición, generación de clientes, administración de la API, pruebas unitarias y automatización o simplificación de muchas otras tareas
    • Arquitectura de la API mejorada. En el enfoque code-first, la API se desarrolla método a método, por lo que un desarrollador puede perder fácilmente la pista de la arquitectura general de la API. Sin embargo, con el enfoque spec-first, el desarrollador se ve obligado a interactuar con una API desde la posición de consumidor de la misma, lo que con frecuencia puede ayudarle a diseñar una arquitectura de API más limpia
    • Desarrollo más rápido: como todo el código base se genera automáticamente, no tendrás que escribirlo, lo único que necesitas es desarrollar la lógica de negocio de tu aplicación en particular.
    • Obtienes sugerencias de forma más rápida: los consumidores pueden obtener una visión de la API inmediatamente y pueden ofrecer sugerencias de forma más sencilla, simplemente modificando la especificación.

    ¡Vamos a desarrollar nuestra API con un enfoque spec-first!

    Plan

    1. Desarrollo de la especificación en swagger
      • Docker
      • Localmente
      • Online
    2. Carga de la especificación en IRIS
      • API management REST API
      • ^REST
      • Clases
    3. ¿Qué pasó con nuestra especificación?
    4. Implementación
    5. Desarrollos posteriores
    6. Consideraciones
      • Parámetros especiales
      • CORS
    7. Carga de la especificación en IAM  

    Desarrollo de la especificación

    El primer paso es, naturalmente, escribir la especificación. InterSystems IRIS es compatible con la Open API Specification (OAS):

    OpenAPI Specification (anteriormente Swagger Specification) es un formato de descripción de las APIs para API REST. Un archivo OpenAPI te permite describir toda tu API, incluyendo:

    • Endpoints disponibles (/users) y operaciones en cada endpoint (GET /users, POST /users)
    • Los parámetros de operación entrada y salida, para cada operación
    • Métodos de autenticación
    • Información de contacto, licencia, términos de uso y otro tipo de información

    Las especificaciones de la API se pueden escribir en YAML o JSON. El formato es fácil de aprender y leer tanto para los humanos como para las máquinas. La especificación completa de OpenAPI se puede encontrar en GitHub: Especificación OpenAPI 3.0

      - de la documentación de Swagger.

    Utilizaremos Swagger para escribir nuestra API. Hay varias formas de utilizar Swagger:

    Después de instalar/ejecutar Swagger, deberías ver esta ventana en un navegador web:

    A la izquierda se edita la especificación de la API; y a la derecha se ve inmediatamente la herramienta de prueba/documentación de la API representada de una forma visual.

    Vamos a cargar nuestra primera especificación de la API en él (en YAML). Se trata de una API sencilla con una solicitud GET que devuelve un número aleatorio en un rango especifico.

     
    Especificación Math API
    swagger: "2.0"
    info:
      description: "Math"
      version: "1.0.0"
      title: "Math REST API"
    host: "localhost:52773"
    basePath: "/math"
    schemes:
      - http
    paths:
      /random/{min}/{max}:
        get:
          x-ISC_CORS: true
          summary: "Get random integer"
          description: "Get random integer between min and max"
          operationId: "getRandom"
          produces:
          - "application/json"
          parameters:
          - name: "min"
            in: "path"
            description: "Minimal Integer"
            required: true
            type: "integer"
            format: "int32"
          - name: "max"
            in: "path"
            description: "Maximal Integer"
            required: true
            type: "integer"
            format: "int32"
          responses:
            200:
              description: "OK"

    Esto es en lo que consiste.

    Información básica sobre nuestra API y la versión de OAS utilizada.

    swagger: "2.0"
    info:
      description: "Math"
      version: "1.0.0"
      title: "Math REST API"
    

    Host del servidor, protocolo (http, https) y nombres de las aplicaciones web:

    host: "localhost:52773"
    basePath: "/math"
    schemes:
      - http
    

    A continuación, especificamos una ruta (por lo que la URL completa sería http://localhost:52773/math/random/:min/:max) y el método de solicitud HTTP (get, post, put, delete):

    paths:
      /random/{min}/{max}:
        get:
    

    A continuación, especificamos la información sobre nuestra solicitud:

          x-ISC_CORS: true
          summary: "Get random integer"
          description: "Get random integer between min and max"
          operationId: "getRandom"
          produces:
    
          - "application/json"
          parameters:
    
          - name: "min"
            in: "path"
            description: "Minimal Integer"
            required: true
            type: "integer"
            format: "int32"
    
          - name: "max"
            in: "path"
            description: "Maximal Integer"
            required: true
            type: "integer"
            format: "int32"
          responses:
            200:
              description: "OK"
    

    En esta parte definimos nuestra solicitud:

    • Habilitar esta ruta para CORS (explicaremos esto más adelante)
    • Proporcionar summary y description
    • operationId permite una referencia dentro de las propias especificaciones, además es un nombre de método generado en nuestra clase de implementation
    • produces: formato de respuesta (como text, xml, json)
    • parameters especifica los parámetros de entrada (ya sea en la URL o en el cuerpo); en nuestro caso especificamos 2 parámetros, el rango para nuestro generador de números aleatorios
    • responses lista de respuestas posibles del servidor

    Como ves, este formato no es especialmente difícil, aunque hay muchas más funciones disponibles, aquí hay una especificación.

    Por último, vamos a exportar nuestra definición como JSON. Ir a File  Convert y guardar como JSON. La especificación debería tener este aspecto:

     
    Especificación Math API
    {
      "swagger": "2.0",
      "info": {
        "description": "Math",
        "version": "1.0.0",
        "title": "Math REST API"
      },
      "host": "localhost:52773",
      "basePath": "/math",
      "schemes": [
        "http"
      ],
      "paths": {
        "/random/{min}/{max}": {
          "get": {
            "x-ISC_CORS": true,
            "summary": "Get random integer",
            "description": "Get random integer between min and max",
            "operationId": "getRandom",
            "produces": [
              "application/json"
            ],
            "parameters": [
              {
                "name": "min",
                "in": "path",
                "description": "Minimal Integer",
                "required": true,
                "type": "integer",
                "format": "int32"
              },
              {
                "name": "max",
                "in": "path",
                "description": "Maximal Integer",
                "required": true,
                "type": "integer",
                "format": "int32"
              }
            ],
            "responses": {
              "200": {
                "description": "OK"
              }
            }
          }
        }
      }
    }

    Carga de la especificación en IRIS

    Ahora que tenemos nuestra especificación, podemos generar el código base para esta API REST en InterSystems IRIS.

    Para pasar a esta etapa necesitaremos tres cosas:

    • Nombre de la aplicación REST: paquete para nuestro código generado (digamos math)
    • La especificación OAS en formato JSON: la acabamos de crear en un paso anterior
    • Nombre de la aplicación WEB: una ruta base para acceder a nuestra API REST (/math en nuestro caso)

    Hay tres maneras de utilizar nuestra especificación para generar códigos, que son esencialmente lo mismo y solo ofrecen varias maneras de acceder a la misma función

    1. Llamar a la rutina ^%REST (Do ^%REST en una sesión terminal interactiva), documentación.
    2. Llamar a la clase %REST (Set sc = ##class(%REST.API).CreateApplication(applicationName, spec), forma no interactiva), documentación.
    3. Utilizar API Management REST API, documentación.

    Creo que la documentación describe adecuadamente los pasos necesarios, así que solo tienes que seleccionar uno. Añadiré dos notas:

    • En los casos (1) y (2) puedes transmitir a un objeto dinámico, un nombre de archivo o una URL
    • En los casos (2) y (3) debes realizar una llamada adicional para crear una aplicación WEB: set sc = #class(%SYS.REST). DeployApplication(restApp, webApp, authenticationType), así que en nuestro caso set sc = ##class(%SYS.REST). DeployApplication("math", "/math"), obtenemos los valores del argumento authenticationType desde el archivo include%sySecurity, las entradas correspondientes son $$$Authe*, por lo que para el acceso no autenticado transmitimos $$$AutheUnauthenticated. Si se omite, el parámetro se ajusta de forma predeterminada a la autenticación por contraseña.

    ¿Qué pasó con nuestra especificación?

    Si has creado la aplicación con éxito, debería crearse un nuevo paquete math con tres clases:

    • Spec: almacena la especificación tal y como está.
    • Disp: se llama directamente cuando se invoca el servicio REST. Se ocupa de la gestión del manejo de REST y llama a los métodos de implementación.
    • Impl: contiene la implementación interna real del servicio REST. Solo deberías editar esta clase.

    Documentación con más información sobre las clases.

    Implementación

    Inicialmente nuestra implementation classmath.impl contiene solo un método, que corresponde a nuestra operación /random/{min}/{max}:

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

    Comenzaremos con la implementación sencilla:

    ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject
    {
        quit {"value":($random(max-min)+min)}
    }
    

    Y finalmente podemos llamar a nuestra API REST abriendo esta página en el navegador: http://localhost:52773/math/random/1/100

    El resultado debería ser:

    {
        "value": 45
    }
    

    También en el editor Swagger al presionar el botón Try it out, y rellenando los parámetros de la solicitud, también se enviaría la misma solicitud:

    ¡Enhorabuena! ¡Nuestra primera API REST creada con un enfoque spec-first ya está funcionando!

    Desarrollos posteriores

    Por supuesto, nuestra API no es estática y tenemos que agregar nuevas rutas, y así sucesivamente. Con el desarrollo spec-first, empiezas por modificar la especificación, después actualizas la aplicación REST (las mismas llamadas que para crear la aplicación) y finalmente escribes el código. Ten en cuenta que las actualizaciones de la especificación son seguras: tu código no se verá afectado, incluso si la ruta se elimina de una especificación, el método no se eliminará en la implementation class.

    Consideraciones

    ¡Más notas!

    Parámetros especiales

    InterSystems añadió parámetros especiales a la especificación de swagger. Son estos:

    <td>
      <b>Tipo de datos</b>
    </td>
    
    <td>
      <b>Predeterminado</b>
    </td>
    
    <td>
      <b>Lugar</b>
    </td>
    
    <td>
      <b>Descripción</b>
    </td>
    
    <td>
      classname
    </td>
    
    <td>
      %CSP.REST
    </td>
    
    <td>
      información
    </td>
    
    <td>
      Superclase para la clase <i>dispatch</i>.
    </td>
    
    <td>
      booleano
    </td>
    
    <td>
      falso
    </td>
    
    <td>
      operación
    </td>
    
    <td>
      Marca para indicar que las solicitudes CORS para esta combinación de endpoint/método deben ser soportadas.
    </td>
    
    <td>
      matriz
    </td>
    
    <td>
       
    </td>
    
    <td>
      operación
    </td>
    
    <td>
      Lista separada por comas de los recursos definidos y sus modos de acceso (resource:mode) que se requieren para acceder a este endpoint del servicio REST. Por ejemplo: ["%Development:USE"]
    </td>
    
    <td>
      cadena
    </td>
    
    <td>
       
    </td>
    
    <td>
      operación
    </td>
    
    <td>
      Nombre del método de clase llamado en el back end para dar servicio a esta operación; de forma predeterminada es operationId, que normalmente es el más adecuado.
    </td>
    
    Nombre
    x-ISC_DispatchParent
    x-ISC_CORS
    x-ISC_RequiredResource
    x-ISC_ServiceMethod

     

    CORS

    Hay tres maneras de activar el soporte de CORS.

    1. En cada ruta, especificando x-ISC_CORS como verdadero. Eso es lo que hemos hecho en nuestra API REST Math.

    2. En cada API, añadiendo

      Parameter HandleCorsRequest = 1;

    y recompilando la clase. También sobreviviría a la actualización de las especificaciones.

    1. (Recomendada) En cada API, mediante la implementación de la superclase custom dispatcher (debe extender %CSP.REST), y escribiendo la lógica del procesamiento de CORS allí. Para utilizar esta superclase, añade x-ISC_DispatchParent a tu especificación.
       

    Carga de la especificación en IAM

      Por último, vamos a añadir nuestra especificación en IAM para que sea publicada por otros desarrolladores.

    Si no has comenzado con IAM, consulta este artículo. También explica cómo publicar APIs REST por medio de IAM, por eso no lo describimos aquí. Es posible que quieras modificar los parámetros spec host y basepath para que apunten a IAM, en lugar de a la instancia de InterSystems IRIS.

    Abre el portal del Administrador de IAM y ve a la pestaña Specs en el espacio de trabajo correspondiente.

    Haz clic en el botón Add Spec e introduce el nombre de la nueva API (math en nuestro caso). Después de crear nuevas especificaciones en IAM, haz clic en Editar y pega el código de las especificaciones (JSON o YAML, no importa para IAM):

    No olvides hacer clic en Update file.

    Ahora nuestra API está publicada para los desarrolladores. Abre el Developer Portal y haz clic en Documentation en la esquina superior derecha. Además de las tres API predeterminadas, nuestra nueva API REST Math debería estar disponible:

    Ábrela:

    ¡Ahora los desarrolladores pueden ver la documentación de nuestra nueva API y probarla en el mismo lugar! 

     

    Conclusiones

    InterSystems IRIS simplifica el proceso de desarrollo de una API REST y el enfoque spec-first permite una gestión más rápida y sencilla del ciclo de vida de la API REST. Con este enfoque, puedes utilizar una variedad de herramientas para una variedad de tareas relacionadas, como la generación de clientes, las pruebas unitarias, la administración de la API y muchas otras más.

    Enlaces

    1
    0 960
    Artículo Ricardo Paiva · jun 24, 2021 6m read

    Publicación Original por: Anton Umnikov
    Arquitecto Senior de soluciones en la nube en InterSystems
    AWS CSAA, GCP CACE

    AWS Glue es un proceso ETL (extraer, transformar y cargar) completamente gestionado, que hace sencillo y rentable clasificar los datos, limpiarlos, enriquecerlos y moverlos de forma fiable entre diferentes almacenes de datos.

    En el caso de InterSystems IRIS, AWS Glue permite mover grandes cantidades de datos a IRIS desde fuentes de datos tanto en la nube como en las propias instalaciones (on-premise). Las fuentes de datos potenciales incluyen, pero no se limitan a, bases de datos on-prem, archivos CSV, JSON, Parquet y Avro que residen en buckets S3, bases de datos nativas en la nube como AWS Redshift y Aurora, y muchas otras.

    Este artículo asume que tienes un conocimiento básico de AWS Glue, al menos en el nivel para completar los Tutoriales de introducción a AWS Glue . Nos centraremos en los aspectos técnicos de la configuración de los trabajos (Jobs) en AWS Glue para utilizar InterSystems IRIS como Datos Objetivo, o en otros términos, "receptor de datos".

    Imagen de https://docs.aws.amazon.com/glue/latest/dg/components-key-concepts.html
     

    Los Trabajos en AWS Glue se ejecutan "sin servidor". Todos los recursos necesarios para realizar el trabajo son suministrados de forma dinámica por AWS solo durante el tiempo en que el trabajo se ejecuta realmente; y se destruyen en el momento en que el trabajo se completa. Por eso, en lugar de provisionar, gestionar e incurrir en costes continuos por la infraestructura necesaria, se te factura solo por el tiempo en que el trabajo realmente se ejecuta y puedes dedicar tus esfuerzos a escribir el código del Trabajo. Durante el tiempo de "inactividad" no se consumen recursos, salvo los buckets S3, que almacenan el código del Trabajo y la configuración.

    Aunque hay varias opciones disponibles, normalmente los Trabajos en Glue se ejecutan con un suministro dinámico en Apache Spark y se escriben en código PySpark. El Trabajo en Glue contiene la sección _"Extraer"_ (donde los datos se extraen de las fuentes de datos), una serie de _"Transformaciones"_ (construidas utilizando la API de Glue) y finalmente la parte _"Cargar"_ o _"receptor"_, en la que después de la transformación final los datos se escriben en el sistema objetivo.

    Para permitir que AWS Glue interactúe con IRIS necesitamos asegurar lo siguiente:

    • Glue tiene acceso a la red de las instancias IRIS involucradas
    • El archivo JAR del controlador JDBC de IRIS es accesible para el Trabajo en Glue
    • El Trabajo en Glue utiliza la API compatible con InterSystems IRIS JDBC

    Examinemos cada uno de los pasos necesarios.

    Crear una conexión a IRIS

    En la consola AWS, selecciona AWS Glue->Connections->Add Connection

    Introduce el nombre de tu conexión y selecciona "JDBC" como Tipo de Conexión.

    En la URL de JDBC introduce la cadena de conexión JDBC para tu instancia de IRIS, el nombre de usuario y la contraseña.

    El siguiente paso es crucial, necesitas asegurarte de que Glue coloca sus endpoints en el mismo VPC que tu instancia de IRIS. Selecciona la VPC y la Subred de tu instancia de IRIS. Cualquier grupo de seguridad con una regla de entrada de autorreferencias para todos los puertos TCP se ejecutaría aquí. Por ejemplo, el grupo de seguridad de tu instancia de IRIS.

    Rol de IAM con acceso al controlador JDBC

    Si aún no lo has hecho, carga el archivo JAR del controlador JDBC de IRIS intersystems-jdbc-3.0.0.jar en el bucket S3. En este ejemplo, estoy usando el bucket s3://irisdistr. Sería distinto para tu cuenta.

    Necesitas crear un rol de IAM para tu Trabajo en Glue, que pueda acceder a ese archivo, junto con otros buckets S3 que Glue utilizaría para almacenar scripts, registros, etc.

    Asegúrate de que tiene acceso de lectura al bucket de tu controlador JDBC. En este caso, concedemos a esta función (GlueJobRole) acceso de solo lectura (ReadOnly) a todos los buckets, junto con el AWSGlueServiceRole predefinido. Puedes elegir limitar aún más los permisos de este rol.

    Crear y configurar el Trabajo en Glue

    Crear un nuevo Trabajo en Glue. Selecciona el rol de IAM, que creamos en el paso anterior. Deja todo lo demás de forma predeterminada.

    En "Security configuration, script libraries, y job parameters (optional)" establece en "Dependent jars path" la ubicación del intersystems-jdbc-3.0.0.jar  en el bucket S3.

     

    Para la fuente: utiliza una de tus fuentes de datos preexistentes. Si seguiste los tutoriales mencionados más arriba, ya tendrás al menos una.

    Utiliza la opción "Create tables in your data target" y selecciona la conexión IRIS que has creado en el paso anterior. Deja todo lo demás de forma predeterminada.

    Si nos has seguido hasta ahora, deberías llegar a una pantalla similar a esta:

     

    ¡Ya queda poco! Solo necesitamos hacer un sencillo cambio en el script para cargar datos en IRIS.

    Cómo ajustar el script

    El script que Glue generó para nosotros utiliza AWS Glue Dynamic Frame, extensión propietaria de AWS para Spark. Aunque ofrece algunos beneficios para los trabajos de ETL, también garantiza que no puedes escribir datos en ninguna base de datos para la que AWS no haya administrado la oferta de servicios.

    Buenas noticias- en el momento de escribir los datos en la base de datos ya no son necesarios todos los beneficios de Dynamic Dataframe, como no aplicación de esquema para los datos "sucios" (en el momento de escribir los datos se supone que están "limpios") y podemos convertir fácilmente Dynamic Dataframe a un Dataframe nativo en Spark que no está limitado a los objetivos administrados por AWS y puede trabajar con IRIS.

    Así que la línea que necesitamos cambiar es la línea 40 de la imagen más arriba. Un último consejo.

    Este es el cambio que necesitamos hacer:

    #datasink4 = glueContext.write_dynamic_frame.from_jdbc_conf(frame = dropnullfields3, catalog_connection = "IRIS1", connection_options = {"dbtable": "elb_logs", "database": "USER"}, transformation_ctx = "datasink4")
    dropnullfields3.toDF().write \
        .format("jdbc") \
        .option("url", "jdbc:IRIS://172.30.0.196:51773/USER/") \
        .option("dbtable", "orders") \
        .option("user", irisUsername) \
        .option("password", irisPassword) \
        .option("isolationlevel","NONE") \
        .save()

    Donde irisUsername e irisPassword son el nombre de usuario y las contraseñas para tu conexión JDBC en IRIS.

    Nota: almacenar las contraseñas en el código fuente NO es conveniente. Te animamos a utilizar herramientas como AWS Secrets Manager para ello, pero entrar en este nivel de detalles de seguridad está más allá del alcance de este artículo. Este es un buen artículo sobre el uso de AWS Secrets Manager en AWS Glue.

    Ahora pulsa el botón "Run Job", siéntate y relájate mientras AWS Glue efectúa el proceso ETL por ti.

    Bueno... lo más probable es que cometas algunos errores al principio... Todos sabemos cómo funciona. Una errata por aquí, un puerto equivocado en el grupo de seguridad por allí... AWS Glue utiliza CloudWhatch para almacenar todos los registros de ejecución y de errores. Busca grupos de registro en: /aws-glue/jobs/error y _ /aws-glue/jobs/output_, para identificar lo que salió mal.

    ¡Disfruta de la programación de los procesos ETL en la nube!

    1
    0 495