Limpiar filtro
Artículo
Mathew Lambert · 31 jul, 2020
¡Hola desarrolladores!
Después de trabajar un poco con IRIS, queremos compartir con vosotros la "caja de herramientas" para InterSystems IRIS: ToolBox-4-Iris.
¿En qué consiste?
ToolBox-4-Iris es una API para IRIS que incluye un conjunto de herramientas muy útiles, no disponibles en IRIS y que simplifican enormemente el desarrollo de aplicaciones. Permite ahorrar tiempo y esfuerzo en las "herramientas típicas" que todo desarrollador necesita. Esto incluye clases adicionales, métodos individuales o incluso macros más eficientes, que se describen en sus respectivos paquetes.
Contenido
MacrosMacros generales de ObjectScript, macros de estado, macros de National Language Support y macros de JavaScript.
Tipos de datosTipos de datos dinámicos adicionales para fechas, horas y registro de horas, que incluyen formato, ocupación de memoria, SQL, rendimiento y más.
Entrada y salida de datosFunciones útiles para trabajar con archivos y directorios, incluyendo funciones generales para archivos y directorios, buscar contenidos en archivos y directorios, eliminar archivos y directorios, serializar instancias de objetos hacia/desde JSON y más.
Gestión de eventos del lado del servidorFuncionalidad que permite desarrollar aplicaciones que reaccionen de forma dinámica a eventos del sistema o de los datos. Si bien IRIS cuenta con gestión de eventos basados en procesos, no tiene capacidad para responder de forma dinámica a eventos del sistema o de los datos (sólo mediante la implementación de disparadores).Incluye la gestión sincrónica o asincrónica de eventos por procesos del usuario o cola de eventos, uso sincrónico o asincrónico de eventos del sistema o gestión de eventos de datos persistentes.
Utilidades varias Utilidades como funciones de calendario (año bisiesto, semana, día de la semana, días festivos, etc.), formateo libre de fecha, hora y registros de hora, funciones para ejecutar comandos del sistema operativo y alguna utlidad JSON (para copiar, revisar, comparar, exportar, importar, leer, escribir, etc.)..
¡Esperamos ansiosos vuestros comentarios! Y que ToolBox-4-Iris os resulte útil.
Anuncio
Esther Sanchez · 26 oct, 2020
¡Hola Comunidad!
Como sabéis, mañana martes 27 de octubre comienzan las Sesiones Técnicas ("Focus Sessions") del Virtual Summit. Más de 100 sesiones, de 20 minutos cada una, durante tres días, sobre mejores prácticas, nuevas tecnologías y hojas de ruta.
Queremos comentaros que, al final de cada jornada de Sesiones Técnicas (martes, miércoles y jueves), habrá una sesión de Preguntas y Respuestas con los Product Managers de InterSystems. Y cualquiera puede participar en las sesiones y enviar sus preguntas. ¿Te animas?
Para inscribirte a las sesiones de Preguntas y Respuestas ("Live Q&A"):
1. Regístrate o inicia sesión en el Virtual Summit. Irás a un lobby virtual.
2. Haz clic en el botón de "Focus Sessions" para entrar en el auditorio virtual.
3. Haz clic en Data Platform 1. Allí verás el horario de todas las sesiones de Preguntas y Respuestas ("Live Q&A sessions"):
➡️ Día 1: Martes, 27 de octubre
NA/LATAM/EMEA
UTC Time
Boston Time
Live Q&A with Data Platform Team
4:40 PM
12:40 PM
➡️ Día 2: Miércoles, 28 de octubre
NA/LATAM/EMEA
UTC Time
Boston Time
Live Q&A with Data Platform Team
4:40 PM
12:40 PM
➡️ Día 3: Jueves, 29 de octubre
NA/LATAM/EMEA
UTC Time
Boston Time
Live Q&A with Data Platform Team
4:40 PM
12:40 PM
Nota: Puedes enviar tus preguntas por adelantado a VS2020questions@InterSystems.com
Aprovechad para resolver vuestras dudas
(Si aún no os habéis registrado en el Virtual Summit 2020, podéis hacerlo aquí >>)
Pregunta
Bernabé Martín · 9 dic, 2020
Hola Comunidad,
Tengo un Cache 5.2 instalado en una maquina Virtualbox con XP y otra con WIN7, para programas personales. En las dos maquinas de tanto en tanto me sale este error (adjunto al final) cuando ejecuto desde el CUBO “Portal de Gestión de Sistema”.
Para solucionarlo reinstalas todo y vuelve a funcionar hasta que decide dejar de funcionar.
Solicito de la comunidad algún atajo para no tener que gestionar toda la engorrosa maniobra de reinstalarlo todo de nuevo, por suerte no se utiliza muy a menudo la Gestión del Sistema.
La version que tienes instalada es bastante antigua - lo adecuado sería el upgrade a la version más reciente de InterSystems IRIS. La puedes descargar desde Docker Hub. Es totalmente gratuita y tiene la funcionalidade de Cache actualizada y ampliada. Con respecto al caso que comentas lo más usual es que tenga que ver con el web server o bien con alguna dll relacionada.Lo que podrías hacer es:1) reiniciar el servicio 'web server for ...' desde las herramientas administrativas de windows;2) Si no te va con el 1), baja el servicio herramientas administrativas. Abre la consola de administrador y reinicia el apache con el listner en el puerto que usas. Algo como: httpd -k start -n INSTANCIACACHEhttpd -c "Listen 8972"Si tras hacer todo esto sigues con el error, te recomiendo una consulta al soporte WRC. Gracias,
No funciona he parado el servicio apache "web server for cache"
Yo no soy especialista en el sistema toda mi vida profesional he sido lo antiguamente llamado "analista/programador", lo cual quiere decir que ya tengo unos años y por ello tengo esta versión.
Miraré el tema del IRIS para utilizarlo como Cache.
Reitero gracias y salud.
Miraré Ya he comentado antes que yo me quedé en MUMPS.
He instalado el contenedor con IRIS (en un MAC), funciona pero tengo una primera dificultad, para mis necesidades necesito utilizar un TERMINAL más potente (por ejemplo Wrq Reflection), u otro de licencia libre.
No he llegado aún a la importacion de RUTINAS Y GLOBALES y para ella tendré que generar los NAMESPACE.
Dada mi situación de jubilado, todo esto, solo tiene el sentido de la inquietud y la invstigación personal.
Gracias y salud
Artículo
Muhammad Waseem · 15 nov, 2021
En mis artículos anteriores, mostré los pasos para conectar y recuperar datos de Caché desde Appeon PowerBuilder usando ODBC.
En este artículo, mostraré cómo guardar datos en Caché con Appeon PowerBuilder (https://www.appeon.com/products/powerbuilder) usando ODBC.
Estoy usando Company.cls de Samples-Data (https://github.com/intersystems/Samples-Data/tree/master/cls/Sample)
¡Empecemos!
Paso 1 : En primer lugar, debemos establecer una conexión (https://community.intersystems.com/post/connecting-cach%C3%A9-appeon-powerbuilder-using-odbc)Paso 2 : Necesitamos crear un objeto de ventana de datos (datawindow object), que se vinculará a la clase Company.
En el menú File, selecciona New y elige el objeto de ventana de datos Freeform en la pestaña DataWindow.
Paso 3: Selecciona SQL Select de la lista de fuentes de datos.
Paso 4: Selecciona sample.company de la lista de tablas:
Paso 5 : Selecciona las columnas deseadas en la lista de columnas y haz clic en Return.
Paso 6: Esto abrirá una vista de diseño. Guarda la ventana de datos (datawindow) como d_company_entry después de los ajustes deseados.
Paso 7: Asegúrate de anular la selección de la columna id en la lista de columnas sin fecha (Updatable columns) y selecciona id en la lista desplegable del campo Identity Column, ya que la identificación se generará automáticamente desde Caché.
Paso 8: En el control de ventana, añade el objeto de ventana de datos (d_company_entry) que ya creamos al control de ventana de datos
Paso 9: Ahora necesitamos insertar una fila en el control de ventana de datos usando la función Insertrow(0) del control de ventana de datos.
Paso 10: Añade algunos datos usando el control de ventana de datos de PowerBuilder.
Paso 11: Guarda los datos en el control de la ventana de datos utilizando la función update() del control de la ventana de datos.
Eso es todo. Se añade el ID 21
Artículo
Ricardo Paiva · 18 nov, 2021
Hablando con mi amigo @Renato.Banzai, especialista en Machine Learning, me expuso uno de los mayores retos a los que se enfrentan actualmente las empresas: la implementación del Machine Learning (ML) y la Inteligencia Artificial (IA) en entornos reales.
Intersystems IRIS ofrece IntegratedML. IntegratedML es una excelente herramienta para practicar, probar y realizar implementaciones de modelos de ML e IA.
La parte más complicada de crear ML/IA es procesar los datos, depurarlos y hacerlos fiables.
¡Ahí es donde podemos aprovechar el estándar FHIR!
La idea del proyecto muestra cómo podemos crear/practicar/validar modelos ML/IA con FHIR y utilizarlos con datos de distintas fuentes.
Creemos que este proyecto tiene un gran potencial y también hay algunas ideas que pueden analizarse:
* Reutilizar/ampliar las transformaciones DTL en otras bases de datos FHIR para modelos de ML personalizados
* Utilizar las transformaciones DTL para normalizar los mensajes FHIR y publicar los modelos ML como servicios
* Crear un tipo de modelos + un repositorio con las reglas de las transformaciones para utilizarlos en cualquier conjunto de datos FHIR
Si exploramos nuevas posibilidades de este proyecto, imaginemos datos de distintas fuentes.

Como se muestra en la imagen anterior, el recurso FHIR, que consume la API REST, se puede utilizar con un FHIRaaS.
Y no solo es posible utilizar FHIRaaS en AWS, sino que además podemos aprovechar el nuevo servicio de [HealthShare Message Transformation Services](https://aws.amazon.com/marketplace/pp/prodview-q7ryewpz75cq2?sr=0-9&ref_=beagle&applicationId=AWSMPContessa), que automatiza la conversión de HL7v2 a FHIR® para ingresar datos en Amazon HealthLake, donde podrás extraer más valor para tus datos.
Con estas sencillas demostraciones, creo que estos recursos pueden utilizarse muy bien en escenarios más grandes, permitiendo implementaciones más sencillas en entornos de producción verdaderamente novedosos, como el de [AWS Healthlake](https://aws.amazon.com/healthlake/). ¿Por qué no? 😃
Artículo
Henrique Dias · 25 mayo, 2022
¡Hola Comunidad!
@José.Pereira y yo queremos presentaros ZPM Explorer, nuestra interfaz gráfica para explorar las excelentes aplicaciones que hay en InterSystems Package Manager.
## La idea
La idea de un explorador de ZPM es facilitar a las personas encontrar lo que ZPM ofrece. Cada semana, cada día, una nueva app se une al mundo ZPM, así que... ¿por qué no ayudar a los desarrolladores y no-desarrolladores a aprovechar las ventajas de este increíble mundo?!
## La aplicación
ZPM es sencillo y potente, así que intentamos trasladar esa sencillez a algo fácil de usar, ofreciendo una búsqueda potente, para hacer más fácil que nunca localizar una aplicación específica, descubrir nuevas apps, encontrar nuevas soluciones... con un simple clic.
La página principal del Explorador de ZPM es una tabla de datos con la información ofrecida por el *endpoint* [https://pm.community.intersystems.com/packages/-/all](https://pm.community.intersystems.com/packages/-/all)
Los campos son:
- Name: el nombre de la aplicación
- Descripción: descripción de lo que hace la aplicación
- Repository: enlace al repositorio en Github
- Version: versión actual de la aplicación dentro de Package Manager

## Cómo instalar nuevas apps
El uso de la app es bastante sencillo.
1. Busca la aplicación que necesitas
2. Selecciona la aplicación
3. Haz clic en el botón Install
4. Confirma
5. Hecho
6. Empieza a hacer un buen uso de ella
## Cómo gestionar las aplicaciones instaladas

ZPM Explorer ofrece una página para gestionar las apps existentes instaladas a través de ZPM.
Puedes actualizar, eliminar e incluso usar un asistente para que sea más sencillo crear tu propio module.xml, especialmente la etiqueta Dependencies.

## Demo
[ZPM Explorer Demo](https://www.youtube.com/watch?v=24nlYIH83iY)
.
Artículo
Alberto Fuentes · 11 abr, 2025
Debido a que la interpretación de SCHEMA por parte de MySQL difiere de la comprensión interpretación común en SQL (como se ve en IRIS, SQL Server u Oracle), nuestro asistente automático de tablas vinculadas puede encontrar errores al intentar recuperar la información de metadatos para construir la tabla vinculada.
(Esto también se aplica a procedimientos y vistas vinculadas)
Al intentar crear una tabla vinculada mediante el asistente, os encontraréis con un error que se parece a esto:
ERROR #5535: SQL Gateway catalog table error in 'SQLPrimaryKeys'. Error: ' SQLState: (HY000) NativeError: [0] Message: [MySQL][ODBC 8.3(a) Driver][mysqld-5.5.5-10.4.18-MariaDB]Support for schemas is disabled by NO_SCHEMA option
Para crear una tabla vinculada a una base de datos MySQL que emplea una estructura “sin esquema” (comportamiento predeterminado), seguid las instrucciones que aparecen a continuación:
Cread una conexión SQL Gateway:
Configurad la conexión SQL Gateway como de costumbre.
Aseguraos de que la casilla "No usar identificadores delimitados por defecto" esté marcada.
Haced clic en "Probar conexión" para confirmar que la conexión es exitosa.
Usad la API basada en Terminal para crear la tabla vinculada:
Utilizad la siguiente API: $SYSTEM.SQL.Schema.CreateLinkedTable() El método CreateLinkedTable() utiliza los siguientes parámetros:
CreateLinkedTable(dsn As %String, externalSchema As %String, externalTable As %String, primaryKeys As %String, localClass As %String = "User.LinkedClass", localTable As %String, ByRef columnMap As %String = "")
Ejemplo: En este ejemplo, usamos la tabla del sistema de MySQL help_keyword con name como clave primaria
USER>do $SYSTEM.SQL.Schema.CreateLinkedTable("MyDSN", "", "help_keyword", "name", "User.LinkedClass", "LocalTable")
Aseguraos de que todos los parámetros estén especificados correctamente para evitar cualquier error durante el proceso de configuración.
En cualquier caso, ¿estáis utilizando actualmente tablas vinculadas? os recomiendo que echéis un vistazo su evolución en InterSystems IRIS, las Foreign Tables.
Artículo
Ricardo Paiva · 14 nov, 2019
¡Hola Comunidad!
Suponga que desarrolló su propia aplicación con la tecnología de InterSystems y ahora quiere realizar varias implementaciones en sus distintos clientes. Durante el proceso de desarrollo usted escribió una guía de instalación detallada para aplicarla, ya que no solo necesita importar clases, también configurar el entorno de acuerdo a sus necesidades.
Para atender esta tarea específica, InterSystems creó una herramienta especial llamada %Installer. Siga con la lectura para saber cómo utilizarla.
%Installer
Con esta herramienta, podrá definir el manifiesto de instalación, que describe la configuración de Caché que se desea, en lugar de los pasos para su instalación. Lo único que debe hacer es describir lo que quiere, y Caché generará automáticamente el código necesario para modificar el entorno por usted. Por lo tanto, solo debe distribuir el manifiesto en sí, mientras que todo el código de instalación se generará para el servidor específico de Caché en el momento de la compilación.
Para definir un manifiesto, cree un nuevo bloque XData con una descripción detallada de la configuración de destino e implemente un método para generar el código en Caché ObjectScript para este bloque XData (este código siempre es el mismo). Una vez que el manifiesto esté listo, puede acceder a él desde la consola/terminal o bien desde código Caché ObjectScript, o automáticamente durante la instalación de Caché. El manifiesto debe ejecutarse en el namespace %SYS. Los manifiestos pueden manejar tanto parámetros del sistema (superport, OS, directorio mgr, etc.) como parámetros arbitrarios proporcionados por el usuario. En general, cada clase de instalación debe cumplir los siguientes requisitos:
Contener un enlace para %occInclude.inc
Contener un bloque XData con la configuración del servidor de Caché
El bloque puede tener cualquier nombre que sea válido
Agregar [XMLNamespace = INSTALLER] después del nombre del bloque, si es necesario consulte las indicaciones de Studio
Llamar al elemento raíz (solo debe tener uno) <Manifest> que incluye a todos los demás elementos
También debe implementar el método setup(), el cual generará el código que necesite el programa para el bloque XData.
Conocimientos básicos sobre el instalador
Puede ejecutar un manifiesto de instalación de varias formas:
En el namespace %SYS desde la consola/terminal o desde código en Caché ObjectScript
do ##class(MyPackage.MyInstaller).setup()
Se realiza automáticamente durante la instalación de Caché. Para ello, exporte la clase del instalador en DefaultInstallerClass.xml que está almacenada en la carpeta con el paquete de instalación de Caché (por ejemplo, donde se almacenan setup_cache.exe o cinstall). Durante la instalación de Caché, esta clase se importará al namespace %SYS y se ejecutará mediante el método setup().
Ejemplo
Consideremos un ejemplo sencillo. Establezca la clase App.Installer que contiene un instalador, el cual generará un nuevo namespace con el nombre definido por el usuario, después creará la aplicación web predeterminada e importará el código a este nuevo namespace:
Include %occInclude
Class App.Installer {
/// You can see generated method in zsetup+1^App.Installer.1
XData Install [ XMLNamespace = INSTALLER ]
{
<Manifest>
<If Condition='(##class(Config.Namespaces).Exists("${Namespace}")=0)'>
<Log Text="Creating namespace ${Namespace}" Level="0"/>
<Namespace Name="${Namespace}" Create="yes" Code="${Namespace}" Ensemble="0" Data="${Namespace}">
<Configuration>
<Database Name="${Namespace}" Dir="${MGRDIR}${Namespace}" Create="yes"/>
</Configuration>
</Namespace>
<Log Text="End Creating namespace ${Namespace}" Level="0"/>
</If>
<Role Name="AppRole" Description="Role to access and use the App" Resources="%DB_CACHESYS:RW,%Admin_Secure:U" />
<Namespace Name="${Namespace}" Create="no">
<CSPApplication Url="/csp/${Namespace}" Directory="${CSPDIR}${Namespace}" AuthenticationMethods="64" IsNamespaceDefault="true" Grant="AppRole" />
<IfDef Var="SourceDir">
<Log Text="SourceDir defined - offline install from ${SourceDir}" Level="0"/>
<Import File="${SourceDir}"/>
</IfDef>
</Namespace>
</Manifest>
}
///Entry point method, you need to call
/// At class compile time it generate Caché ObjectScript code from the manifest
/// After that you can run this installer from a terminal:
/// Set pVars("Namespace")="NewNamespace"
/// Set pVars("SourceDir")="C:\temp\distr\"
/// Do ##class(App.Installer).setup(.pVars)
ClassMethod setup(ByRef pVars, pLogLevel As %Integer = 0, pInstaller As %Installer.Installer) As %Status [ CodeMode = objectgenerator, Internal ]
{
Quit ##class(%Installer.Manifest).%Generate(%compiledclass, %code, "Install")
}
}
En este ejemplo, el instalador realiza las siguientes acciones:
Comprueba si existe un namespace con el mismo nombre que el valor de la variable Namespace (para que quede claro, especificaremos que la variable Namespace se estableció en NewNamespace)
Si no es así, entonces registrará la creación de un nuevo namespace llamado NewNamespace
Definir un nuevo namespace:
El nombre es NewNamespace
Crea un nuevo namespace
La base de datos para las rutinas es NewNamespace
No activa Ensemble
La base de datos para globales es NewNamespace
Crea una nueva base de datos
Su nombre es NewNamespace;
La crea en la carpeta mgr/NewNamespace (tenga en cuenta que la variable MGRDIR está disponible de forma predeterminada)
La creación del namespace está completa y registrada
Crea una nueva función: AppRole (con los recursos %DB_CACHESYS:RW y %Admin_Secure:U)
Crea una nueva aplicación web predeterminada /csp/NewNamespace (también asigna AppRole de forma automática)
Si la variable SourceDir está definida, entonces importa todos los archivos desde allí a NewNamespace
Para que este instalador se inicie en un terminal, ejecute los siguientes comandos:
Set pVars("Namespace")="NewNamespace"
Set pVars("SourceDir")="C:\temp\distr\"
Do ##class(App.Installer).setup(.pVars)
Durante la ejecución el terminal muestra información importante:
2016-02-17 19:26:17 0 App.Installer: Installation starting at 2016-02-17 19:26:17, LogLevel=0
2016-02-17 19:26:17 0 : Creating namespace NewNamespace
2016-02-17 19:26:17 0 : End Creating namespace NewNamespace
2016-02-17 19:26:17 0 : SourceDir defined - offline install from C:\temp\distr\
2016-02-17 19:26:18 0 App.Installer: Installation succeeded at 2016-02-17 19:26:18
2016-02-17 19:26:18 0 %Installer: Elapsed time .545148s
Para recibir aún más información sobre lo que está sucediendo, especifique LogLevel (de 0 (default) a 3 (raw); más alto = más información).
Do ##class(App.Installer).setup(.pVars, 3)
Ahora hablaremos sobre las cosas que pueden hacerse en el manifiesto de instalación.
Nodos disponibles
Un manifiesto se compone de los siguientes elementos:
Nodo
Nodo padre
Atributos (valores predeterminados)
Descripción
Arg
Invoke, Error
Value – es el valor de un argumento
Pasa un argumento hacia un método que se llama mediante Invoke o Error
ClassMapping
Configuration
Package - un paquete que debe mapearse
From - nombre de la base de datos de origen que se utilizó para el mapeo
Crea un mapeo de clases desde una base de datos hacia el namespace que contiene el elemento Configuration
Compile
Namespace
Class - son los nombres de las clases para compilación
Flags - son marcas de compilación (ck)
IgnoreErrors - se utiliza para ignorar errores (0)
Son la clases de los compiladores. Llama a $System.OBJ.Compile(Class, Flags)
Configuration
Namespace
Se utiliza para crear namespaces y bases de datos. Cierra las etiquetas que activan los mapeos y actualiza el archivo CPF
CopyClass
Namespace
Src - es la clase de origen
Target - es la clase de destino
Replace - elimina la clase de origen (0)
Copia o desplaza la definición de la clase de origen a la de destino
CopyDir
Manifest
Src - es el directorio fuente
Target - es el directorio de destino
IgnoreErrors - se utiliza para ignorar errores (0)
Copia un directorio
CopyFile
Manifest
Src - es el archivo de origen
Target - es el archivo de destino
IgnoreErrors - se utiliza para ignorar errores (0)
Copia un archivo
Credential
Production
Name - es el nombre de las credenciales de acceso
Username - es el nombre de usuario
Password - es la contraseña de usuario
Overwrite - sobrescribe si la cuenta ya existe
Crea o sobreescribe las credenciales de acceso
CSPApplication
Namespace
AuthenticationMethods - activa los métodos de autenticación
AutoCompile - realiza compilaciones automáticas (en la configuración CSP)
CSPZENEnabled - la marca CSP/ZEN
ChangePasswordPage - es la ruta para cambiar la contraseña de la página
CookiePath - ruta hacia la sesión de cookies
CustomErrorPage - ruta para personalizar la página de error
DefaultSuperclass - es una superclase personalizada
DefaultTimeout - se agotó el tiempo en espera de la sesión
Description - realiza descripciones
Directory - ruta hacia los archivos CSP
EventClass - nombre de la clase del evento
Grant - lista de funciones asignadas en el momento que el sistema actualiza su registro
GroupById - realiza agrupaciones mediante las propiedades del Id
InboundWebServicesEnabled - marca de los servicios web entrantes
IsNamespaceDefault - es la marca de la aplicación para un Namespace predeterminado
LockCSPName - Bloqueo de la marca del nombre CSP
LoginClass - ruta para acceder a la página de inicio de sesión
PackageName - nombre del paquete de propiedades
PermittedClasses - clases de propiedades permitidas
Recurse - indicador de recursión (sirve a los subdirectorios) (0)
Resource - recurso requerido para acceder a la aplicación web
ServeFiles - propiedad de los archivos de servicio
ServeFilesTimeout - es el tiempo, en segundos, que le toma a Chaché almacenar los archivos estáticos.
TwoFactorEnabled - es una autenticación de dos pasos
Url - es el nombre de la aplicación web
UseSessionCookie - utiliza las cookies para la sesión
Crea o modifica una aplicación web. Para obtener más detalles, consulte la documentación y la clase Security.Applications
Database
Configuration
BlockSize - tamaño del bloque en bytes de la base de datos
ClusterMountMode - se encarga de organizar la base de datos como parte del cluster
Collation - orden de la clasificación
Create - si desea crear una nueva base de datos (yes/no/overwrite (yes))
Dir - es el directorio
Encrypted - cifrado de la base de datos
EncryptionKeyID - ID de la clave de cifrado
ExpansionSize - tamaño en MB que puede expandirse
InitialSize - tamaño inicial
MaximumSize - tamaño máximo
MountAtStartup - organización después del lanzamiento
MountRequired - especifica que la base de datos DEBE organizarse con éxito en el momento del inicio
Nombre - es el nombre de la base de datos
PublicPermissions - son los permisos públicos
Resource - es el recurso
StreamLocation - es el directorio a donde se dirigen los flujos que están asociados a esta base de datos.
Crea o modifica una base de datos. Para obtener más información, consulte la documentación y las clases Config.Databases y SYS.Database
Default
Manifest
Name - es el nombre de la variable
Value - es el valor de la variable
Dir - valor de la variable (si es una ruta hacia una carpeta/archivo)
Establece el valor de la variable (si aún no se estableció)
Else
Manifest, Namespace
Puede ejecutarse cuando la sentencia if es falsa
Error
Manifest
Status - código de error
Source - origen del error
Envía una excepción. Tenga en cuenta que la sintaxis para ${} y #{} no está disponible
ForEach
Manifest
Index - es el nombre de la variable
Values - una lista con los valores para la variable
Collection-es un bucle controlado
GlobalMapping
Configuration
Global - es el nombre del global
From - es el nombre de la base de datos para el mapeo
Collation - orden de la clasificación (Caché por defecto)
Mapea un global
If
Manifest, Namespace
Condition - es una sentencia condicional
Sentencia condicional if
IfDef
Manifest, Namespace
Var – nombre de la variable
La sentencia condicional if se utiliza cuando la variable ya fue establecida
IfNotDef
Manifest, Namespace
Var – nombre de la variable
La sentencia condicional if se utiliza cuando la variable aún no fue establecida
Import
Namespace
File - archivo/carpeta para importación
Flags - marcas de compilación (ck)
IgnorarErrores - se utiliza para ignorar errores (0)
Recurse - importar recursivamente (0)
Importa archivos. Llama a:
$System.OBJ.ImportDir(File,,Flags,,Recurse) y $System.OBJ.Load(File, Flags)
Invoke
Namespace
Class - nombre de la clase
Method - nombre del método
CheckStatus - comprueba el estado de la respuesta
Return - escribe el resultado en una variable
Hace una llamada a un método de una clase con varios argumentos y devuelve los resultados de la ejecución
LoadPage
Namespace
Name: ruta a la página CSP
Dir - es una carpeta con páginas CSP
Flags - indicadores de compilación (ck)
IgnoreErrors - se utiliza para ignorar errores (0)
Carga archivos CSP mediante $System.CSP.LoadPage(Name, Flags) y $System.CSP.LoadPageDir(Dir, Flags)
Log
Manifest
Level - nivel de registro desde 0 (mínimo) hasta 3 (detallado)
Text - cadena con una longitud de hasta 32,000 caracteres
Añade un mensaje al registro cuando el nivel de registro es mayor o igual al atributo "level"
Manifest
Elemento de la raíz. Es el único elemento de la raíz en un manifiesto, contiene todos los demás elementos
Namespace
Manifest
Name - nombre del namespace
Create - si se debe crear un nuevo namespace (yes/no/overwrite (yes))
Code - base da datos para el código del programa
Data - base de datos
Ensemble - activa Ensemble para el namespace
Todos los demás atributos pueden utilizarse con las aplicaciones web de Ensemble
Define el alcance del instalador
Production
Namespace
Nombre - nombre de la producción
AutoStart - lanzamiento automático de la producción
Configura la producción en Ensemble
Resource
Manifest
Name - nombre del recurso
Description - descripción del recurso
Permission - permisos públicos
Crea o modifica un recurso.
Role
Manifest
Name - nombre del role
Description - descripción de la función (no debe contener comas)
Resources - son los recursos asignados a la función, se representan como "MyResource:RW,MyResource1:RWU"
RolesGranted - si se otorgan las funciones correspondientes
Crea un nuevo role
RoutineMapping
Configuration
Routines - nombre de la rutina
Type - tipos de rutinas (MAC, INT, INC, OBJ o ALL)
From - base de datos de origen
Crea un nuevo mapeo para las rutinas
Setting
Production
Item - elemento que puede configurarse
Target - tipos de parámetros: Item, Host, Adapter
Setting - nombre del parámetro
Value - valor del parámetro
Configura un elemento en la producción de Ensemble. Hace una llamada al método Ens.Production:ApplySettings
SystemSetting
Manifest
Name - class.property del paquete Config
Value - valor del atributo
Establece los valores para los atributos del paquete Config (usando el método Modify)
User
Manifest
Username - nombre de usuario
PasswordVar - variable que contiene la contraseña
Roles - lista de funciones del usuario
Fullname - nombre completo
Namespace - espacio de nombres de inicio
Routine - rutina de inicio
ExpirationDate - fecha después de la cual el usuario será desactivado
ChangePassword - cambie la contraseña al iniciar sesión por última vez en el sistema
Enabled - si el usuario está activo
Crea o modifica un usuario.
Var
Manifest
Name - nombre de la variable
Value - valor de la variable
Asigna un valor a la variable
Variables
Variables suministradas por el usuario
Algunos atributos pueden contener expresiones (cadenas) que aumentan cuando el manifiesto se ejecuta. Existen tres tipos de expresiones que aumentarían y son parecidas a las siguientes:
${<Variable_name>} – es el valor de la variable (definida por el usuario o una variable de entorno, consulte más adelante) que se calcula durante la ejecución del manifiesto,
${#<Parameter_name>} – se sustituirá por el valor del parámetro especificado desde la clase del instalador durante la compilación,
#{<Caché_ObjectScript_code>} — es el valor de la sentencia que se especificó en Caché ObjectScript y se calculará durante la ejecución del manifiesto. Asegúrese de poner comillas según sea necesario.
Los valores de los parámetros se definen durante la compilación y, por lo tanto, pueden formar parte de una variable, o de una sentencia de Caché ObjectScript. Dado que las variables se interpretan antes que el código de Caché ObjectScript, puede utilizarlas en las sentencias de Caché ObjectScript, por ejemplo: #{$ZCVT("${NAMESPACE}","L")}.
Variables del sistema
Las siguientes variables siempre están disponibles:
Variable
Descripción
Ejemplo del valor
SourceDir
(Disponible solo cuando se ejecuta el instalador) Directorio desde el que se ejecuta la instalación (setup_cache.exe o cinstall).
/InterSystems/distr/
ISCUpgrade
(Disponible solo cuando se ejecuta el instalador) Indica si se trata de una instalación nueva o de una actualización. Cuando esta variable es 0 se considera una instalación nueva, o 1 cuando es actualización.
0 (instalación)
1 (actualización)
CFGDIR
Consulte INSTALLDIR.
/InterSystems/Cache/
CFGFILE
Ruta al archivo CPF
/InterSystems/Cache/cache.cpf
CFGNAME
Es el nombre de la instancia
CACHE
CPUCOUNT
Número de núcleos en el CPU
4
CSPDIR
Es el directorio CSP
/InterSystems/Cache/csp/
HOSTNAME
Es el nombre del servidor web
SCHOOL15
HTTPPORT
Es el puerto del servidor web
80
INSTALLDIR
Es el directorio donde se instaló Caché
/InterSystems/Cache/
MGRDIR
Es el directorio de administración (mgr)
/InterSystems/Cache/mgr/
PLATFORM
Es el sistema operativo
UNIX
PORT
Es el puerto del super servidor de Caché
1972
PROCESSOR
Es el nombre de la plataforma
x86-64
VERSION
Es la versión de Caché
2015.1.1
Depuración de errores
Algunas veces es difícil entender qué valores pueden asignarse como valores de atributos en los nodos. Para averiguarlo, compruebe el código int generado para el método de instalación. En la mayoría de los casos, la llamada principal se realiza a tInstaller.<ElementName> que es un objeto de la clase %Installer.Installer que, a su vez, hará llamadas directas a los métodos del sistema. Alternativamente, puede comprobar el código de %Installer.class<ElementName> en la que los atributos del nodo son propiedades de la clase. El código del programa se genera en los métodos %OnBeforeGenerateCode, %OnGenerateCode y %OnAfterGenerateCode.
Con fines de depuración, recomiendo que coloque una llamada en una transacción dentro del instalador. Por ejemplo, puede utilizar los comandos TSTART/TROLLBACK para deshacer fácilmente todos los cambios realizados dentro de Caché (sin embargo, los cambios externos, como crear un archivo nuevo para la base de datos, no se revertirán).
Por último, no olvide configurar LogLevel en 3.
Ejemplos
El proyecto MDX2JSON proporciona un instalador. Para instalar el proyecto, importe el archivo installer.xml que contiene la clase MDX2JSON.Installer en cualquiera de los siguientes formatos namespace. Puede realizar la importación desde SMP o arrastrando y soltando el archivo en Studio.
Entonces ejecute el siguiente comando en un terminal:
do ##class(MDX2JSON.Installer).setup()
Como resultado, Caché cargará los archivos de la aplicación desde el repositorio GitHub y luego realizará la instalación en la base de datos predeterminada MDX2JSON namespace/MDX2JSON, mapeará el paquete MDX2SJON a %All y SAMPLES, mapeará el ^MDX2SJON global a %All y SAMPLES, creará la aplicación REST llamada /MDX2JSON, y así sucesivamente, verá todos estos pasos en el terminal. Para obtener información más detallada sobre el instalador de MDX2JSON, consulte el proyecto Léame.
Ejemplos adicionales
Ejemplo de los documentos de apoyo .
La clase Sample.Installer en el namespace Samples.
Los proyectos CacheGitHubCI proporcionan un instalador .
El proyecto SYSMON que se encuentra en el panel de controles proporciona un instalador .
El proyecto DeepSee Audit proporciona un instalador.
Resumen
%Installer es una herramienta conveniente para distribuir e implementar aplicaciones basadas en InterSystems Caché y Ensemble.
Referencias
Documentos de apoyo
Artículo
Nancy Martínez · 20 ene, 2020
¡Hola Comunidad!
HealthShare utiliza muchas transformaciones XSL. Estas transformaciones se utilizan para convertir los documentos médicos de la iniciativa “Integración de las Empresas Sanitarias (IHE)” en SDA (formatos interno de HealthShare), y convertirlos nuevamente en los formatos IHE, con el fin de crear resúmenes de los informes y para lidiar con los perfiles en el IHE (por ejemplo, consultar la información de los pacientes, proporcionar documentos y registrarlos). Los clientes pueden configurar las XSLT para personalizar los informes, o para utilizarlas de alguna otra manera.
Para la depuración y el desarrollo es muy conveniente que se pueda ejecutar un XSLT desde el Terminal.
El método de clase
A continuación, se muestra una clase que contiene un método de clase, que permitirá ejecutar un XSLT desde el terminal en Windows. Previamente, se debe crear la clase en HSREGISTRY o en algún otro namespace en HealthShare (no utilizar HSLIB o VIEWERLIB), y procede a la compilación.
Class Local.XsltTransformer Extends %RegisteredObject
{
ClassMethod Transform(XslDirectory As %String, XslBaseFilename As %String, Directory As %String, InputFilename As %String, OutputFilename As %String, byref Parameters = "")
{
// Run the XSLT transform with the base filename XslBaseFilename (i.e., without the .xsl
// extension) that is in the XslDirectory. Run it on the input file with name InputFilename
// and put the output in the file with name OutputFilename. The input file must be in the
// directory Directory, and the output will be put in the same directory. The Parameters
// argument may be used to pass parameters to the transform (rarely needed). This class
// method should be run from Terminal in a HealthShare namespace other than HSLIB or
// VIEWERLIB. The method will write out the path and name of the transform and any error
// messages.
set In = ##class(%Stream.FileCharacter).%New()
set In.Filename = Directory _ "\" _ InputFilename
set Out = ##class(%Stream.FileCharacter).%New()
set Out.Filename = Directory _ "\" _ OutputFilename
set Transformer = ##class(HS.Util.XSLTTransformer).%New()
set Transformer.XSLTCacheMode = "N"
set Transformer.XSLTDirectory = XslDirectory
write !, XslDirectory _ "\" _ XslBaseFilename _ ".xsl"
set Status = Transformer.Transform( .In, XslBaseFilename _ ".xsl", .Out, .Parameters )
if $system.Status.IsOK( Status ) {
set Status = Out.%Save()
}
if $system.Status.IsError( Status ) {
write $system.Status.GetErrorText( Status )
}
}
}
Los siguientes son los parámetros del método de la clase Transformar: XslDirectory es el directorio donde está el archivo XSLT. La clase primero intentará añadir \Custom al directorio cuando busque la transformación, después intentará hacerlo sin él. XslBaseFilename es el nombre del archivo para la transformación XSL, pero sin la extensión .xsl. Directory es el directorio en el que se encuentra el documento de entrada y donde se quiere que la transformación transcriba los datos de salida de dicha transformación. InputFilename es el nombre del archivo del documento de entrada, incluida su extensión. OutputFilename es el nombre del archivo del documento de salida, incluida su extensión. Los parámetros pueden utilizarse para pasar parámetros la transformación, pero pocas veces son necesarios.
Cómo ejecutar la transformación en el terminal
Para ejecutar una transformación XSL, abre el Terminal, cambia el namespace y ejecuta el método Transformar. Por ejemplo, vamos a ejecutar la transformación de CCDA a SDA que viene con HealthShare. Tengo instalado a HealthShare en C:\InterSystems\HealthShare2016.1.1. Pondré un CCDA llamado "Sample_CCDA.xml" en mi carpeta c:\Junk.
USER>zn "hsregistry"
HSREGISTRY>do ##class(Local.XsltTransformer).Transform( "C:\InterSystems\HealthShare2016.1.1\CSP\xslt\SDA3", "CCDA-to-SDA", "c:\Junk", "Sample_CCDA.xml", "SDA_Out.xml" )
C:\InterSystems\HealthShare2016.1.1\CSP\xslt\SDA3\CCDA-to-SDA.xsl
HSREGISTRY>
El archivo de salida SDA_Out.xml ahora se encuentra en c:\Junk.
Se debe tener en cuenta que no se debe utilizar las transformaciones que se encuentran en el directorio CSP\xslt\SDA. Se debe utilizar las transformaciones que se encuentran en el directorio CSP\xslt\SDA3. Las transformaciones que se encuentran en el directorio SDA son para una versión antigua de SDA (la versión 2).
Asistente para transformaciones Studio XSL
Una alternativa a ejecutar tus transformaciones desde el Terminal es utilizar el Asistente para transformaciones Studio XSL. En Studio, selecciona la ruta Herramientas > Complementos > Asistente para transformaciones XSL. Introduce el archivo de entrada en el campo "XML File" y la transformación XSLT en el campo "XSL File". Para el campo "XSLT Helper Class", seleccione "HSREGISTRY" y "HS.Util.XSLTHelper". Hacer clic en "Finalizar". El resultado se visualizará en la ventana de diálogo (podrás copiarlo y pegarlo):
Depuración de errores
Cuando realices la depuración de errores en las transformaciones XSL, un método para depurar los archivos es añadir elementos <xsl:comment> a las XSLT para que puedas observar varios elementos en la salida. Estos son algunos ejemplos:
<xsl:comment>useFirstTranslation <xsl:value-of select="$useFirstTranslation"/>.
referenceValue <xsl:value-of select="$referenceValue"/>.
displayName <xsl:value-of select="@displayName"/>.
originalText <xsl:value-of select="hl7:originalText/text()"/>.
descriptionValue <xsl:value-of select="$descriptionValue"/>.
</xsl:comment>
<xsl:comment>Context node:</xsl:comment>
<xsl:copy-of select="." />
<xsl:comment>End of context node.</xsl:comment>
Varios de los XSLT de HealthShare envían una solicitud a una plantilla de "Canonización". Por ejemplo, CCDA-to-SDA.xsl esto se hace cuando veas el comentario "Canonizar la salida SDA". La canonización eliminará los comentarios desde la salida, por ello es posible que desee realizar los comentarios durante la depuración.
Documentación
Para obtener más información sobre los XSLT en HealthShare, puedes consultar los siguientes capítulos del libro "Descripción general de Health Connect (Overview of Health Connect)", los cuales se encuentran disponibles en los documentos:
Capítulo 6: Documentos CDA y Transformaciones XSL en HealthShare
Capítulo 7: Personalización del CDA y transformaciones en XSL
En particular, en el Capítulo 7 se proporcionan algunas sugerencias para realizar la depuración. Además, puedes consultar el siguiente capítulo del libro "Registros de los intercambios de información":
Capítulo 14: Gestión de los tipos de informes de resumen en XML
Artículo
Pierre-Yves Duquesnoy · 10 mar, 2021
Con el lanzamiento de [PEX](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=EPEX) en InterSystems IRIS 2020.1 e InterSystems IRIS for Health 2020.1, los clientes tienen una mejor forma de incorporar Java en las producciones que con el Java Business Host. PEX proporciona un completo conjunto de APIs para construir la interoperabilidad de los componentes y está disponible tanto en Java como en .NET. Java Business Host ha sido discontinuado y se retirará en una versión futura.
Ventajas de PEX
* Permite que los desarrolladores creen cualquier componente de producción tanto en Java como en .NET
* Se pueden transferir estructuras de mensajes más complejas entre los componentes
* Configuración simplificada
* Workflow de desarrollo simplificado, sin necesidad de ObjectScript.
El resto de este artículo se centra en cómo migrar el código existente de Java Business Host a PEX.
## Resumen
Las clases y las interfaces que utiliza PEX son diferentes a las de Java Business Host (JBH). Aquí ofrecemos un resumen de las diferencias, pero la [documentación](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=EPEX_apiref) recoge toda la información necesaria.
* [Cómo cambiar un Business Service](#Converting-a-Business-Service-from-Java-Business-Host-to-PEX)
* [Cómo cambiar una Business Operation](#Converting-a-Business-Operation-from-Java-Business-Host-to-PEX)
* [Configuración](#Settings)
* [Mensajes](#Messages)
* [Logging](#Logging)
## Cómo cambiar un Business Service desde Java Business Host a PEX
Para crear un PEX Business Service, debes implementar `com.intersystems.enslib.pex.BusinessService` en vez de `com.intersystems.gateway.bh.BusinessService`.
El patrón de diseño utilizado por PEX para el Business Service ha cambiado de uno en el que se espera que el servicio inicie un subproceso para producir mensajes, a otro en el que el servicio implementa una función que es llamada periódicamente para producir mensajes.
En JBH, tu código se parecería a esto:
```java
@Override
public boolean OnInit(Production p) throws Exception {
production = p;
if (messageThread == null) {
Messager messager = new Messager();
messageThread = new Thread(messager);
messageThread.start();
}
return true;
}
```
En PEX, solo necesitas implementar tres funciones:
```java
public void OnInit() throws Exception {
// Initialization
return;
}
public Object OnProcessInput(Object messageInput) throws Exception {
// Here is where you call SendMessage() or SendMessageAsync()
return null;
}
public void OnTearDown() throws Exception {
// Shut down
return;
}
```
También deberás cambiar la forma en que se utiliza la configuración, se entregan los mensajes y se hace logging. Hablaremos de eso más adelante.
## Cómo cambiar una Business Operation desde Java Business Host a PEX
Para crear una PEX Business Operation, debes implementar `com.intersystems.enslib.pex.BusinessOperation` en vez de `com.intersystems.gateway.bh.BusinessOperation`.
El patrón de diseño para Business Operations es estructuralmente el mismo entre JBH y PEX, pero han cambiado los parámetros a dos puntos de acceso principales.
### Cambios en OnInit()
En PEX, `OnInit()` no requiere de ningún parámetro.
### Cambios en OnMessage()
En PEX, `OnMessage()` recibe un `Object` genérico en vez del `String` usado en JBH. Esto permite al autor de la producción transmitir cualquier tipo de mensaje que desee.
En JBH, tu aplicación pudo haber tenido este aspecto:
```java
public boolean OnMessage(String message) throws Exception {
// Business logic here
return true;
}
```
En PEX, el parámetro es un Java Objetct genérico, que se debe lanzar de forma adecuada, lo que permite transmitir mensajes más complejos que únicamente cadenas. Este es un ejemplo de cómo extraer una solicitud que es una secuencia de archivos:
```java
public Object OnMessage(Object request) throws Exception {
com.intersystems.jdbc.IRISObject streamContainer = (com.intersystems.jdbc.IRISObject)request;
com.intersystems.jdbc.IRISObject str = (com.intersystems.jdbc.IRISObject)streamContainer.get("Stream");
String originalFilename = (String)streamContainer.get("OriginalFilename");
Long contentSize = (Long)str.get("Size");
String content = (String)str.invoke("Read", contentSize);
// Business logic here
return null;
}
```
También deberás cambiar la forma en que se utiliza la configuración, se entregan los mensajes y se hace logging. Hablaremos de eso más adelante.
## Configuración
Se ha simplificado la declaración de la configuración.
La configuración en JBH se declaraba mediante una cadena `SETTINGS` y se obtenía a través de un código que se parece a algo como esto:
```java
String setting = production.GetSetting("Min");
if (!setting.isEmpty()) {
min = Integer.parseInt(setting);
}
```
En PEX, la configuración son solo campos para miembros públicos. Estos se completan automáticamente cuando la clase crea una instancia.
```java
public int Min = 0;
```
Cualquier campo de un miembro público está disponible para que se establezca en una producción, en la medida en que el campo del miembro sea de un tipo básico de Java (String, int, etc.).
## Mensajes
El envío de mensajes es más potente. En JBH, los mensajes se envían como cadenas. En PEX, los mensajes se envían como objetos- IRISObject, para objetos definidos en ObjectScript, o una subclase de `com.intersystems.enslib.pex.Message`, para clases definidas en Java.
En JBH, tu código se parecería a esto:
```java
production.SendRequest(value.toString());
```
En PEX, sería algo como esto:
```java
MyExampleMessageClass req = new MyExampleMessageClass("message to send");
SendRequestAsync(Target, req);
```
## Logging
Todas las funciones de logging son similares, solo que se nombran de manera diferente.
En PEX, se registraría un mensaje informativo a través de `LOGINFO()`
```java
LOGINFO("Received message");
```
## Object Gateway
JBH necesitaba su propio portal. Con PEX, puedes utilizar un único portal de Java para todas tus necesidades de Java. O puedes utilizar varios portales. Depende de ti. Aquí encontrarás una buena [introducción al portal de Java](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=EJVG_intro).
## Conclusión y comentarios
Si aún no has probado PEX, ¿a qué está esperando? PEX permite resolver un conjunto mucho más amplio de problemas empresariales con menos código, además de que ahora también puedes hacer cualquier cosa en .NET.
Si tienes salguna pregunta o problema para migrar tu aplicación de JBH a PEX, puedes contactar conmigo o con el Centro de Soporte Internacional (WRC).
Este artículo está etiquetado como "Mejores prácticas" ("Best practices")
(Los artículos con la etiqueta "Mejores prácticas" incluyen recomendaciones sobre cómo desarrollar, probar, implementar y administrar mejor las soluciones de InterSystems).
Artículo
Ricardo Paiva · 4 sep, 2020
¡Hola desarrolladores!
La clase %Net.SSH.Session permite conectarse a servidores mediante SSH. Lo más habitual es usarlo con SFTP, especialmente en los adaptadores de FTP entrantes y salientes.
En este artículo se dará un breve ejemplo de cómo conectarse a un servidor SSH usando la clase, se describirá las opciones para autenticar y cómo hacer la depuración cuando surjan problemas.
A continuación un ejemplo de cómo hacer la conexión:
~~~
Set SSH = ##class(%Net.SSH.Session).%New()
Set return=SSH.Connect("ftp.intersystems.com")
~~~
Esto crea una nueva conexión, y luego se conecta al servidor SFTP ftp.intersystems.com en el puerto predeterminado. En este punto, el cliente y el servidor han elegido opciones y algoritmos de cifrado, pero ningún usuario ha iniciado sesión aún.
Una vez conectado, podrá elegir cómo realizar la autenticación. Hay tres métodos principales para elegir:
- AuthenticateWithUsername
- AuthenticateWithKeyPair
- AuthenticateWithKeyboardInteractive
Cada uno de estos es un tipo distinto de autenticación. La siguiente es una breve introducción a cada tipo:
#### AuthenticateWithUsername
Esta usa un nombre de usuario y contraseña.
#### AuthenticateWithKeyPair
Esta usa un par de claves pública y privada. La clave pública se debe haber precargado en el servidor, y debe contar con la clave privada correspondiente. Si la clave privada está cifrada en el disco, debe introduzir una contraseña para descifrarla en la llamada al método. Nota: nunca envíe su clave privada a otra persona.
Las claves públicas deben estar en formato OpenSSH, y las claves privadas deben estar cifradas con PEM. El formato OpenSSH se ve así:
~~~
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCfi2Vq+u0rtt2OC84pyrkq1k7WkrS+s76u3a+2gdD43KQ2Z3vSUUfksymJjp11JBZEpOtBVIAy221UKdc7j7Qk6sUjZaK8LIy+bzDVwMyFWgVvQge7EjdWjrJLBRCDXYML6y1Y25XexThkTWSGyXzGNdr+wfIHYn/mIt0hfvrusauvT/9Wz8K2MGAj4BL7UQZpFJrlXzGmewe6++6cZDQQYi0aztwLK798oc9j0LsccdMpqWrjqoU1uANFhYIuUu/T47TEhT+e6M+KFYK5TR998eJTO25IjdN2Tgw0feXhQFF/nngbol0bA4auSPaZQsgokKK+E+Q/8UtBdetEofuV user@hostname
~~~
Las claves privadas cifradas con PEM tienen un encabezado en la parte superior del archivo que se ve así:
~~~
-----BEGIN RSA PRIVATE KEY-----
~~~
y terminan con:
~~~
-----END RSA PRIVATE KEY-----
~~~
#### AuthenticateWithKeyboardInteractive
Permite realizar una autenticación de desafío y respuesta. Por ejemplo, podría pedir el código de un uso enviado por mensaje de texto o generado por una aplicación autenticadora de Google. Para usar este tipo de autenticación, deberá escribir una función lambda para manejar la solicitudes de comandos enviadas por el servidor.
Puede que vea que algunos servidores usan esto con solo una solicitud de nombre de usuario y contraseña, de una forma que para el usuario se ve idéntica a una autenticación por contraseña. Las marcas de depuración SSH descritas a continuación pueden ayudarle a determinar si eso es lo que está viendo.
Un último comentario sobre la autenticación: Si le interesa usar dos formas de autenticación para una única conexión, asegúrese de usar Ensemble/Cache 2018.1+ o cualquier versión de InterSystems IRIS. Esta versión tiene actualizaciones que permiten el uso de múltiples formatos, tales como par de claves y nombre de usuario.
## Qué hacer cuando algo sale mal...
### Algunos errores comunes que podría encontrarse son:
#### Error al intentar obtener el banner
Esto podría verse así:
~~~
ERROR #7500: SSH Connect Error '-2146430963': SSH Error [8010100D]: Failed getting banner [FFFFFFFF8010100D] at Session.cpp:231,0
~~~
Obtener el banner es lo primero que hace un cliente SSH. Si ve este error, debería verificar que se está conectando al servidor correcto y que este es un servidor SFTP.
Por ejemplo: si el servidor es en realidad un servidor FTPS, verá este error. Los servidores FTPS usan SSL, no SSH, y por lo tanto no funcionan con la clase %Net.SSH.Session. Puede usar la clase %Net.FtpSession class para conectarse a un servidor FTPS.
#### No es posible intercambiar claves de cifrado
Este error podría verse así:
~~~
ERROR #7500: SSH Connect Error '-2146430971': SSH Error [80101005]: Unable to exchange encryption keys [80101005] at Session.cpp:238,0
~~~
Este error generalmente significa que el cliente y el servidor no pudieron negociar algoritmos de MAC o cifrado. Si ve este error, puede que necesite actualizar ya sea el cliente o el servidor para agregar compatibilidad con nuevos algoritmos.
Si está usando una versión de Ensemble/Caché anterior a la 2017.1, le recomiendo actualizar a InterSystems IRIS o probar con 2017.1 o posterior. La biblioteca libssh2 se actualizó en la versión 2017.1 y se agregaron múltiples algoritmos nuevos.
Puede ver más detalles en los registros provistos por las marcas de depuración que describo a continuación.
#### Firma inválida para clave pública suministrada
~~~
Error [80101013]: Invalid signature for supplied public key, or bad username/public key combination [80101013] at Session.cpp:418
~~~
Este error podría ser fácil de malinterpretar. Verá este error si su servidor pidió dos formas de autenticación y usted solo facilitó una. Si ese es el caso, continue y pruebe con la próxima. Es posible que todo se arregle.
#### Error -37
Puede ver mensajes sobre el error -37. Por ejemplo, aquí está en el registro de depuración:
~~~
[libssh2] 0.369332 Failure Event: -37 - Failed getting banner
~~~
Siempre que aparezca el error -37, la operación que fracasó volverá a intentarse. Este error no es lo que causó la falla final. Busque otros mensajes de error.
### Las marcas de depuración de SSH
Se puede habilitar el registro detallado de conexiones SSH para una conexión mediante las marcas de depuración de SSH. Las marcas se habilitan con el método SetTraceMethod. Este es un ejemplo de una conexión que las usa:
~~~
Set SSH = ##class(%Net.SSH.Session).%New()
Do SSH.SetTraceMask(511,"/tmp/ssh.log")
Set Status=SSH.Connect("ftp.intersystems.com")
~~~
El primer argumento de SetTraceMask le indica qué recolectar. Es una representación decimal de bits. 511 solicita todos los bits excepto el 512, y es la configuración usada más comúnmente. Si desea conocer más acerca de cada bit, están enumerados en la documentación de la clase %Net.SSH.Session.
El segundo argumento le indica en qué archivo colocar la información de registro sobre la conexión. En este ejemplo usé el archivo /tmp/ssh.log, pero puede ingresar cualquier ruta absoluta o relativa que quiera usar.
En el ejemplo anterior, solo ejecuté el método Connect. Si su problema está en la autenticación, deberá ejecutar también el método de autenticación correspondiente.
Luego de ejecutar su prueba, podrá buscar información en el archivo de registro. Si no está seguro de cómo interpretar el archivo de registro, el Centro Mundial de Respuesta de Intersystems (WRC) puede ayudar.
Artículo
Ricardo Paiva · 4 nov, 2021
En este artículo describiré los procesos para ejecutar pruebas unitarias mediante ObjectScript Package Manager (consulta ), incluyendo el cálculo de la Cobertura de pruebas (mediante ).
## Pruebas unitarias en ObjectScript
Ya hay mucha documentación sobre cómo escribir pruebas unitarias en ObjectScript, por lo que no repetiré nada de eso. Puedes consultar el Tutorial de Pruebas Unitarias aquí:
La práctica recomendada es incluir las pruebas Unitarias en algún lugar/carpeta separada en la estructura de fuentes, ya sea simplemente "/pruebas" o algo más sofisticado. Dentro de InterSystems, terminamos usando /internal/testing/unit_tests/ como nuestro estándar *de facto*, lo que tiene sentido porque las pruebas son internas/no distribuibles y hay otros tipos de pruebas además de las unitarias, pero esto podría ser un poco complejo para proyectos sencillos de código abierto. Puedes ver esta estructura en algunos de nuestros repositorios de GitHub.
Desde el punto de vista del flujo de trabajo, esto es súper fácil en VSCode: solo hay que crear el directorio y colocar las clases allí. Con enfoques más antiguos centrados en el servidor para el control de la fuente (los utilizados en Studio), tendrás que mapear este paquete de manera apropiada, y el enfoque para eso varía según la extensión del control de la fuente.
Desde la perspectiva de los nombres de clases para las pruebas unitarias, mi preferencia personal (y la práctica recomendada de mi grupo) es:
UnitTest.<package/class being tested>[.<method/feature being tested>]
Por ejemplo, si las pruebas unitarias son para el Método Foo en la clase MyApplication.SomeClass, la clase de la prueba unitaria se llamaría UnitTest.MyApplication.SomeClass.Foo; si las pruebas fueran para la clase en su totalidad, simplemente sería UnitTest.MyApplication.SomeClass.
## Pruebas unitarias en ObjectScript Package Manager
¡Hacer que ObjectScript Package Manager esté informado de tus pruebas unitarias es sencillo! Basta con añadir una línea como la siguiente a module.xml (tomada de , una bifurcación del excelente paquete matemático de @Peter.Steiwer de Open Exchange, el cual utilizo como un simple ejemplo inspirador):
`<Module><br> ...<br> <UnitTest Name="tests" Package="UnitTest.Math" Phase="test"/><br></Module>`
Lo que todo esto significa es:
* Las pruebas unitarias están en el directorio "tests" debajo de la raíz del módulo.
* Las pruebas unitarias están en el paquete "UnitTest.Math". Esto tiene sentido, porque las clases que se están probando están en el paquete "Math".
* Las pruebas unitarias se ejecutan en la fase "test" en el ciclo de vida del paquete. (También hay una fase de "verificación" en la que podrían ejecutarse, pero esa es una historia para otro día).
### Cómo ejecutar pruebas unitarias
Con las pruebas unitarias definidas como se explicó anteriormente, el administrador de paquetes ofrece algunas herramientas realmente útiles para ejecutarlas. Todavía puedes configurar ^UnitTestRoot, como lo harías normalmente con %UnitTest.Manager, pero probablemente encontrarás las siguientes opciones mucho más fáciles, especialmente si estás trabajando en varios proyectos en el mismo entorno.
Puedes probar todos estas opciones clonando el repositorio objectscript-math enumerado anteriormente y luego cargarlo con `zpm "load /path/to/cloned/repo/"`, o en tu propio paquete reemplazando "objectscript-math" con los nombres de tus paquetes (y nombres de prueba).
Para recargar el módulo y luego ejecutar todas las pruebas unitarias:
`zpm "objectscript-math test"`
Para simplemente ejecutar las pruebas unitarias (sin recargar):
`zpm "objectscript-math test -only"`
Para simplemente ejecutar las pruebas unitarias (sin recargar) y proporcionar una salida detallada:
`zpm "objectscript-math test -only -verbose"`
Para ejecutar un conjunto de pruebas en particular (es decir, un directorio de pruebas, en este caso, todas las pruebas en UnitTest/Math/Utils) sin recargar, y proporcionar una salida detallada:
`zpm "objectscript-math test -only -verbose -DUnitTest.Suite=UnitTest.Math.Utils"`
Para ejecutar un caso particular de prueba (en este caso, UnitTest.Math.Utils.TestValidateRange) sin recargar y proporcionar una salida detallada:
`zpm "objectscript-math test -only -verbose -DUnitTest.Case=UnitTest.Math.Utils.TestValidateRange"`
O, si solo estás resolviendo los problemas de un único método de prueba:
`zpm "objectscript-math test -only -verbose -DUnitTest.Case=UnitTest.Math.Utils.TestValidateRange -DUnitTest.Method=TestpValueNull"`
Cálculo de la Cobertura de pruebas mediante ObjectScript Package Manager
Así que tienes algunas pruebas unitarias, pero ¿son buenas? Calcular la cobertura de pruebas no responderá completamente a esa pregunta, pero al menos ayuda. Presenté esto en la Convención anual (*Global Summit*) de InterSystems, allá por el año 2018 - aquí puedes ver el vídeo: https://youtu.be/nUSeGHwN5pc .
Lo primero que tendrás que hacer es instalar el paquete de cobertura de pruebas:
`zpm "install testcoverage"`
Ten en cuenta que esto no requiere la instalación/ejecución de ObjectScript Package Manager; puedes encontrar más información en Open Exchange:
Dicho esto, puedes aprovechar al máximo la herramienta de cobertura de pruebas si también utilizas ObjectScript Package Manager.
Antes de ejecutar pruebas, debes especificar qué clases/rutinas esperas que cubran tus pruebas. Esto es importante porque, en las bases de código muy grandes (por ejemplo, HealthShare), calcular y recopilar la Cobertura de pruebas para todos los archivos del proyecto puede requerir más memoria de la que tiene tu sistema. (Específicamente, gmheap para un análisis por linea de código, si tienes curiosidad).
La lista de archivos se incluye en un archivo llamado cover.list, que está dentro de la raíz de la prueba unitaria. Diferentes subdirectorios (conjuntos) de pruebas unitarias pueden tener su propia copia de esto para anular las clases/rutinas que se rastrearán mientras se ejecuta el conjunto de pruebas.
Para ver un ejemplo sencillo con objectscript-math, consulta: . La [guía de usuario para la Herramienta de cobertura de pruebas](https://github.com/intersystems/TestCoverage#user-guide) incluye más detalles.
Para ejecutar las pruebas unitarias con el cálculo de la Cobertura de pruebas habilitado, solo hay que añadir un argumento más al comando, especificando que se debe utilizar TestCoverage.Manager en vez de %UnitTest.Manager para ejecutar las pruebas:
`zpm "objectscript-math test -only -DUnitTest.ManagerClass=TestCoverage.Manager"`
La salida (incluso en el modo resumido) incluirá una URL donde podrás ver qué líneas de tus clases/rutinas estaban cubiertas por las pruebas unitarias, así como algunas estadísticas agregadas.
## Siguientes pasos
¿Qué sucede con la automatización de todo esto en CI? ¿Qué sucede con los reportes de resultados de las pruebas unitarias y las puntuaciones/diffs de cobertura? ¡También puedes hacer eso! Para ver un ejemplo sencillo usando Docker, Travis CI y codecov.io, consulta . Estoy planeando escribir esto en un artículo futuro que analice algunos enfoques diferentes.
Artículo
Alberto Fuentes · 25 mayo, 2021
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](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
- de la documentación de Swagger.
Utilizaremos Swagger para escribir nuestra API. Hay varias formas de utilizar Swagger:
* [Online](https://editor.swagger.io/)
* Docker: `docker run -d -p 8080:8080 swaggerapi/swagger-editor`
* [Instalación local](https://swagger.io/docs/open-source-tools/swagger-editor/)
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](https://en.wikipedia.org/wiki/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](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md).
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](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GREST_routine).
2. Llamar a la clase `%REST` (`Set sc = ##class(%REST.API).CreateApplication(applicationName, spec)`, forma no interactiva), [documentación](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_objectscriptapi).
3. Utilizar API Management REST API, [documentación](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_apimgmnt).
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](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST_intro#GREST_intro_classes) con más información sobre las clases.
### Implementación
Inicialmente nuestra *implementation class* `math.impl` contiene solo un método, que corresponde a nuestra operación `/random/{min}/{max}`:
/// Get random integer between min and max<br/>
/// The method arguments hold values for:<br/>
/// min, Minimal Integer<br/>
/// max, Maximal Integer<br/>
ClassMethod getRandom(min As %Integer, max As %Integer) As %DynamicObject
{
//(Place business logic here)
//Do ..%SetStatusCode(<HTTP_status_code>)
//Do ..%SetHeader(<name>,<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:
Nombre
Tipo de datos
Predeterminado
Lugar
Descripción
x-ISC_DispatchParent
classname
%CSP.REST
información
Superclase para la clase dispatch.
x-ISC_CORS
booleano
falso
operación
Marca para indicar que las solicitudes CORS para esta combinación de endpoint/método deben ser soportadas.
x-ISC_RequiredResource
matriz
operación
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"]
x-ISC_ServiceMethod
cadena
operación
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.
#### 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.
3. (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](https://es.community.intersystems.com/node/465921). 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
* [Especificación OpenAPI 3.0](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md)
* [Cómo crear servicios REST](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=GREST)
* [Presentación de IAM](https://es.community.intersystems.com/post/presentaci%C3%B3n-de-intersystems-api-manager)
* [Documentación de IAM](https://docs.intersystems.com/irislatest/csp/docbook/apimgr/index.html)
Este artículo está etiquetado como "Mejores prácticas" ("Best practices").
Los artículos con la etiqueta "Mejores prácticas" incluyen recomendaciones sobre cómo desarrollar, probar, implementar y administrar mejor las soluciones de InterSystems.
Artículo
Alberto Fuentes · 29 jul, 2019
¡Hola a tod@s!
Hoy os traigo un artículo de Kyle Baxter sobre búsquedas de texto libre que vale la pena guardar como referencia :)
¿Os gustaría buscar de forma eficiente campos de texto libres almacenados en vuestra aplicación? ¿Lo habéis intentado alguna vez pero no habéis encontrado una manera que os ofrezca un buen rendimiento? Hay un truco especial que resuelve el problema :)
Como es habitual, si preferís la versión TL;DR (Demasiado largo, no lo he leído), podéis ir directamente al final del artículo, pero preferiríamos que leyeseis el artículo entero para evitar herir sentimientos.
Vamos a utilizar como ejemplo la clase Sample.Company que encontrareis en:
IRIS - incluida en el paquete de ejemplos https://github.com/intersystems/Samples-Data que puedes descargar e instalar
Caché / Ensemble - namespace SAMPLES
La clase Sample.Company tiene el campo Mission que es un texto generado al azar. Para el ejemplo, se han generado alrededor 256,246 empresas.
/// The company's mission statement.
Property Mission As %String(MAXLEN = 200, POPSPEC = "Mission()");
Supongamos que queremos buscar en este campo de texto:
SELECT * FROM Sample.Company WHERE LIKE '%agile%'
Es una consulta bastante razonable, pero ¿cómo funciona? Al no tener un índice, claramente necesita leer cada entrada, de modo que obtenemos 256,277 referencias a globals de nuestras 7,854 filas devueltas. ¡Esto no está bien! Añadamos un índice a Sample.Company y veamos si podemos hacerlo mejor:
Index MissionIndex on Mission;
Construimos el índice y ejecutamos la misma consulta. ¿Qué obtenemos ahora? 279,088 referencias a globals.
Pero... ¡eso son más referencias a globals! ¿Acaso no es malo? ¡Los índices debían habernos ayudado!
Calmémonos. Además, cuando nos enfrentamos a un comportamiento contradictorio, es mejor tomarse un momento para pensar. ¿Cuál es el coste de leer un índice en comparación con leer la tabla de datos completa? Un índice será más pequeño, así que leer el índice MissionIndex completo será una operación menos costosa que hacer un escaneo completo de la tabla. Pero además del índice, necesitaremos leer algunas partes de la tabla para mostrar los resultados. En este caso, aunque haya más referencias a globals, es menos trabajo (asumiendo que todo está en disco). Por supuesto, podemos hablar mucho más sobre este comportamiento y la estructura de bloques de bases de datos de Caché pero eso está fuera del ámbito de este artículo.
De acuerdo, así que nuestra primera modificación y el ahorro de nuestra búsqueda posiblemente funcionaron, pero ciertamente el resultado fue menor de lo que nos gustaría. Buscamos algo mucho más rápido, queremos menos referencias a globals y que sea sencillo de implementar ¿qué podemos hacer? la respuesta es un índice iFind .
Vamos a definir el índice de la siguiente forma:
Index MissioniFind on (Mission) as %iFind.Index.Basic;
Reconstruimos los índices ##class(Sample.Company).%BuildIndices() y ahora necesitamos decirle a la consulta que utilice este nuevo índice:
SELECT * FROM Sample.Company WHERE %ID %FIND search_index(MissioniFind,’agile’)
Ahora, esta consulta obtiene las mismas 7,854 filas, pero lo hace con 7,928 referencias a globals. ¡Son apenas más referencias a globals que filas! Lo cual es algo muy bueno.
¿Podemos combinar esto con otros índices? Sí, las combinaciones de índices funcionan muy bien con esta tecnología.
¿Existe alguna restricción? Pues sí. Necesita que el ID de la clase sea compatible con bitmaps (mapas de bits). Es decir, el ID de la tabla debe ser un número entero positivo.
¿Dónde puedo obtener más información? En la documentación, desde luego:
https://irisdocs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSQLSRCH_txtsrch_indexing
https://irisdocs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?APP=1&LIBRARY=%25SYS&CLASSNAME=%25iFind.Index.Basic
------------------------------------------------------
TL;DR (Demasiado largo, no lo he leído):
Para buscar en un campo de texto libre:
1. Añadir un índice iFind sobre el campo de texto libre:
Index <IndexName> on <FreeTextField> As %iFind.Index.Basic
2. Reconstruir índices de forma habitual, por ejemplo:
do ##class(Sample.Company).%BuildIndices()
3. Re-escribir la consulta SQL de la siguiente manera en la cláusula WHERE:
…WHERE ID %FIND search_index(<IndexName>,<Search Value>) AND …
Este artículo está etiquetado como "Mejores prácticas" ("Best practices").
Los artículos con la etiqueta "Mejores prácticas" incluyen recomendaciones sobre cómo desarrollar, probar, implementar y administrar mejor las soluciones de InterSystems.
Artículo
Alberto Fuentes · 25 ago, 2022
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.
La clase %SYSTEM,Process ofrece varias métricas que puedes recoger para un proceso (además de Duration).
Duration
Lines Executed
Global References
System CPU Time
User CPU Time
Disk Read Time
Para permitir a cualquier prueba unitaria recoger esas estadísticas, crea una subclase de %UnitTest.TestCase y añade propiedades.
Class Performance.TestCase Extends %UnitTest.TestCase
{
Property Duration As %Time;
Property Lines As %Integer;
Property Globals As %Integer;
Property SystemCPUTime As %Integer;
Property UserCPUTime As %Integer;
Property DiskReadTime As %Integer;
}
Cualquier clase de unit test que crees debería heredar de tu nueva subclase en vez de %UnitTest.TestCase.
En la subclase, usa OnBeforeOneTest() para iniciar la recogida de estadísticas para cada prueba unitaria. Para todo excepto DiskReadTime,el código inicia la propiedad con el valor actual.
/// initialize performance stats
Method OnBeforeOneTest(testname As %String) As %Status
{
// initialize with current values
set ..Duration = $zh
set ..Lines = $system.Process.LinesExecuted()
set ..Globals = $system.Process.GlobalReferences()
set ..SystemCPUTime = $piece(CPUTime, ",", 1)
set ..UserCPUTime = $piece(CPUTime, ",", 2)
// reset disk read time to 0 and start counting
do $system.Process.ResetDiskReadTiming()
do $system.Process.EnableDiskReadTiming()
return $$$OK
}
Usa OnAfterOneTest() para terminar la recogida de estadísticas para cada prueba unitaria. Para todo excepto DiskReadTime, el código elimina el valor inicial desde el valor actual.
/// Finalize performance stats
/// This is where you could also add code to save the counters to a separate table for analysis.
Method OnAfterOneTest(testname As %String) As %Status
{
set ..Duration = $zh - ..Duration
set ..Lines = $system.Process.LinesExecuted() - ..Lines
set ..Globals = $system.Process.GlobalReferences() - ..Globals
set CPUTime = $system.Process.GetCPUTime()
set ..SystemCPUTime = $piece(CPUTime, ",", 1) - ..SystemCPUTime
set ..UserCPUTime = $piece(CPUTime, ",", 2) - ..UserCPUTime
// get disk read time and stop counting
set ..DiskReadTime = $system.Process.DiskReadMilliseconds()
do $system.Process.DisableDiskReadTiming()
// add message to unit test log
set msg = "Performance: " _
"Duration: " _ ..Duration _
", Lines: " _ ..Lines _
", Globals: " _ ..Globals _
", System CPU Time: " _ (..SystemCPUTime / 1000) _
", User CPU Time: " _ (..UserCPUTime / 1000) _
", Disk Read Time: " _ (..DiskReadTime / 1000)
do $$$LogMessage(msg)
return $$$OK
}
Hay otro pequeño truco. Puede que quieras ejecutar tus pruebas unitarias con o sin estadísticas de recogida. Así que, el código en el que estás invocando tus pruebas unitarias debe tomar un argumento (podría ser un %Boolean 1 o 0) y de alguna manera pasarlo. Los métodos que realmente ejecutan las pruebas (como RunTest() u otro de los métodos Run*()) toman un array como el 3er argumento, pasado por referencia. Este es un ejemplo:
// create an array to hold the logging argument (1 or 0) and pass it by reference
set p("logging") = logging
do ##class(%UnitTest.Manager).RunTest(test, qualifiers, .p)
Al valor que pasas en la matriz puede accederse en OnBeforeOneTest() y OnAfterOneTest(). Añade esto como la primera línea en ambos métodos:
if (..Manager.UserFields.GetAt("logging") = 0) { return $$$OK }
¡Y ya está!
Espero vuestras preguntas, comentarios e ideas.
Este artículo está etiquetado como "Mejores prácticas" ("Best practices").
Los artículos con la etiqueta "Mejores prácticas" incluyen recomendaciones sobre cómo desarrollar, probar, implementar y administrar mejor las soluciones de InterSystems.