Pregunta Mathew Lambert · oct 22, 2021

Tenemos un escenario bastante complejo pero creo que es sencillo de explicar y que quede claro.

Estamos desarrollando un ejecutador de tareas que corre en un servidor con una timezone indiferente.

Las tareas se tienen que ejecutar cada dia pero a una hora definida para una cierta timezone

Guardamos en base de datos la hora a la que queremos ejecutar la tarea y de que timezone es (no vayas al pozo de guardar en UTC, ya que cuando tengas DST será a horas distintas)

Lo que queremos es que cuando obtengamos nuestra tarea de la DB, convertir el 02:00 Europe/Madrid ya sea a UTC o a local (servidor)

2
0 104
Comentarios Mathew Lambert · sep 22, 2021

Sobreescribiendo el método Read (que tiene un tipo de retorno %CacheString) en una nueva clase que hereda de %Stream.FileCharacter, se obtiene un error de compilacion informando que el tipo de retorno es incorrecto y ha de ser Binary, aun cuando matchea la firma del padre.

Mirando la global de codigo compilado vemos:

^oddCOM("%Stream.FileBinary","m","Read",42)="%Library.Binary"

Despues de más investigaciones encontramos que hay un método generator que mira la definicion de clase OdbcType.

Seteandolo a LONGVARCHAR nos da:

^oddCOM("User.CStream","m","Read",42)="%Library.String"

0
0 113
Artículo Mathew Lambert · sep 1, 2021 2m read

¡Hola a todos!

Hace poco aprendí algo nuevo mientras trabajaba en un problema con el Centro de Soporte Internacional (WRC), y quería compartirlo con todos por si pudiera ayudar a alguien más.

Escenario:

Tienes archivos que se escriben inexplicablemente en una carpeta de tu servidor y, debido a la cantidad de archivos en la carpeta y al rendimiento general del sistema, no es posible trabajar sobre los archivos para localizar el origen.

0
0 1329
Artículo Mathew Lambert · jul 7, 2021 1m read

A veces necesitas importar datos a IRIS de forma rápida y sencilla. Por eso se ha desarrollado un gestor de importación en IRIS.

Esta aplicación permite importar datos en formato JSON y también ofrece una interfaz muy sencilla para transferir datos desde colecciones en MongoDB a globals en IRIS. Nunca ha sido más fácil.

Vamos a ver unos ejemplos.

Importación de JSON

0
0 222
Artículo Mathew Lambert · jun 18, 2021 1m read

Introducción

Este es Iris-key-uploader, un front-end en Angular con su API REST.

El objetivo de este proyecto es importar fácilmente archivos de claves a IRIS desde una interfaz de usuario web.

¿Por qué este proyecto?

Desafortunadamente el panel de IRIS para cambiar la clave no da la oportunidad de subir la licencia.

Panel

Como se puede ver, solo se puede navegar desde el lado del servidor.

¿Qué pasa si no se tiene un acceso directo a él?

Te gustaría tener una página web sencilla para subir la nueva clave y activarla.

Ese es el propósito de este proyecto.

Demo

Demostración

Interfaz de Usuario

http://localhost:52773/keyuploader/index.html

Compilación

Ejecute el servidor

docker-compose up -d

Instalar con ZPM

zpm "install iris-key-uploader"

Observaciones finales

Esto funciona incluso si la instancia no tiene ninguna clave. De hecho, sin clave, IRIS tiene una LU (Unidad de Licencia) disponible.

0
0 108
Artículo Mathew Lambert · feb 15, 2021 9m read

Introducción y motivación {#RobustErrorHandlingandCleanupinObjectScript-IntroductionandMotivation}

Una unidad de código en ObjectScript (pongamos, un ClassMethod) puede producir una gran variedad de efectos secundarios inesperados cuando interactúa con partes del sistema que están fuera de su propio alcance y no han sido depuradas adecuadamente. En forma de lista parcial, se incluyen:

  • Transacciones
  • Locks
  • Dispositivos E/S
  • Cursores SQL
  • Flags de systema y configuración
  • $Namespace
  • Archivos temporales

Utilizar estas importantes funciones del lenguaje sin hacer cleanup adecuadamente y con desarrollo defensivo podría ocasionar que una aplicación, que normalmente funciona correctamente, falle de forma inesperada y sea difícil de debugar. Es fundamental que el código de cleanup funcione correctamente en todos los posibles casos de error, en especial porque es probable que en pruebas superficiales se ignoren los casos de error. En este artículo se detallan varios problemas conocidos, y se explican dos patrones para lograr que los errores se gestionen y eliminen de manera eficiente.

Injerto importante que está parcialmente relacionado: ¿quieres asegurarte de que estás probando todos tus casos hasta el límite? Echa un vistazo a la ¡Herramienta de cobertura para pruebas en Open Exchange!

Trampas a evitar {#RobustErrorHandlingandCleanupinObjectScript-PitfallstoAvoid}

Transacciones {#RobustErrorHandlingandCleanupinObjectScript-Transactions}

Un enfoque natural y simplista de las transacciones consiste en envolver la transacción en un bloque try/catch, con un TRollback en catch, de la siguiente manera:

Try {
    TSTART
    // ... do stuff with data ...
    TCOMMIT
} Catch e {
    TROLLBACK
    // e.AsStatus(), e.Log(), etc.
}

Estando aislados, este código siempre que lo escrito entre TStart y TCommit arroje excepciones en vez de producir Quit cuando se produzca un error es perfectamente válido. Sin embargo, es arriesgado por dos razones:

  • Si otro desarrollador agrega un “Quit” dentro del bloque Try, una transacción se quedará abierta. Sería sencillo saltarse ese cambio durante la revisión del código, especialmente si no es obvio que haya transacciones involucradas en el contexto actual.
  • Si se llama al método con este bloque desde una transacción externa, TRollback restaurará todos los niveles de la transacción.

Un mejor enfoque consiste en rastrear el nivel de la transacción al principio del método y retroceder hasta ese nivel de la transacción al final. Por ejemplo:

Set tInitTLevel = $TLevel
Try {
    TSTART
    // ... do stuff with data ...
    // The following is fine now; tStatus does not need to be thrown as an exception.
    If $$$ISERR(tStatus) {
        Quit
    }
    // ... do  more stuff with data ...
    TCOMMIT
} Catch e {
    // e.AsStatus(), e.Log(), etc.
}
While $TLevel > tInitTLevel {
    // Just roll back one transaction level at a time.
    TROLLBACK 1
}

Locks {#RobustErrorHandlingandCleanupinObjectScript-Locks}

Cualquier código que utilice locks progresivos también debe garantizar que disminuyan los locks en el código de depuración cuando ya no sean necesarios; de lo contrario, dichos locks se mantendrán hasta que el proceso termine. Los locks no deben filtrarse fuera de un método, a menos que la obtención de dicho lock sea un efecto secundario documentado del método.

Dispositivos de E/S {#RobustErrorHandlingandCleanupinObjectScript-IODevices}

De forma similar, los cambios en el dispositivo de E/S actual (la variable especial $io) tampoco deberían filtrarse fuera de un método, a menos que el propósito del método sea cambiar el dispositivo actual (por ejemplo, habilitar la redirección de E/S). Cuando se trabaja con archivos, es preferible utilizar el paquete %Stream en vez del E/S para archivos secuenciales directos por medio de OPEN / USE / READ / CLOSE. En otros casos, donde es necesario utilizar dispositivos de E/S, debe restablecerse el dispositivo original cuando finalice el método. Por ejemplo, el siguiente patrón de código es arriesgado:

Method ReadFromDevice(pSomeOtherDevice As %String)
{
    Open pSomeOtherDevice:10
    Use pSomeOtherDevice
    Read x
    // ... do complicated things with X ...
    Close pSomeOtherDevice
}

Si se lanza una excepción antes de que pSomeOtherDevice esté cerrado, entonces $io se quedará como pSomeOtherDevice; esto probablemente ocasionará errores en cascada. Además, cuando el dispositivo se cierra, $io se restablece al dispositivo predeterminado del proceso, el cual probablemente no sea el mismo dispositivo que se utilizó antes de que se llamara el método.

Cursores SQL {#RobustErrorHandlingandCleanupinObjectScript-SQLCursors}

Cuando se utiliza SQL basado en cursores, el cursor debe estar cerrado en caso de ocurra cualquier error. Cuando el cursor no se cierra, pueden producirse filtraciones en los recursos (según la documentación de apoyo). Además, en algunos casos, si se ejecuta el código de nuevo y se intenta abrir el cursor, se obtendrá un error “already open” (SQLCODE -101).

Flags de sistema y configuración {#RobustErrorHandlingandCleanupinObjectScript-SystemFlagsandSettings}

Excepcionalmente, el código de la aplicación puede necesitar que se modifiquen flags a nivel de proceso o del sistema; por ejemplo, muchos están definidos en %SYSTEM.Process y %SYSTEM.SQL . En todos esos casos, debe tenerse cuidado de almacenar el valor inicial y restablecerlo al final del método.

$Namespace {#RobustErrorHandlingandCleanupinObjectScript-Namespace}

El código que modifica el namespace siempre debe ser New $Namespace al principio, para garantizar que los cambios en el namespace no se filtran fuera del alcance del método.

Archivos temporales {#RobustErrorHandlingandCleanupinObjectScript-TemporaryFiles}

El código de la aplicación encargado de crear archivos temporales, como %Library.File:TempFilename (que, en InterSystems IRIS, realmente crea el archivo), debería eliminar también los archivos temporales cuando ya no se necesiten.

Patrón recomendado: Try-Catch (-Finally) {#RobustErrorHandlingandCleanupinObjectScript-TryCatchFinally}

Muchos lenguajes tienen una función en la que una estructura de tipo try/catch también puede tener un bloque “finally” que se ejecuta cuando se ha completado try/catch, tanto si se produjo una excepción como si no. ObjectScript no lo hace, pero puede aproximarse. Un patrón general para esto, que demuestra muchos de los posibles casos problemáticos, es el siguiente:

ClassMethod MyRobustMethod(pFile As %String = "C:\foo\bar.txt") As %Status
{
    Set tSC = $$$OK
    Set tInitialTLevel = $TLevel
    Set tMyGlobalLocked = 0
    Set tDevice = $io
    Set tFileOpen = 0
    Set tCursorOpen = 0
    Set tOldSystemFlagValue = ""
 
    Try {
        // Lock a global, provided a lock can be obtained within 5 seconds.
        Lock +^MyGlobal(42):5
        If '$Test {
            $$$ThrowStatus($$$ERROR($$$GeneralError,"Couldn't lock ^MyGlobal(42)."))
        }
        Set tMyGlobalLocked = 1
         
        // Open a file
        Open pFile:"WNS":10
        If '$Test {
            $$$ThrowStatus($$$ERROR($$$GeneralError,"Couldn't open file "_pFile))
        }
        Set tFileOpen = 1
         
        // [ cursor MyCursor declared ]
        &;SQL(OPEN MyCursor)
        Set tCursorOpen = 1
         
        // Set a system flag for this process.
        Set tOldSystemFlagValue = $System.Process.SetZEOF(1)
         
        // Do the important things...
        Use tFile
        
        TSTART
        
        // [ ... lots of important and complicated code that changes data here ... ]
         
        // All done!
         
        TCOMMIT
    } Catch e {
        Set tSC = e.AsStatus()
    }
     
    // Finally {
 
    // Cleanup: system flag
    If (tOldSystemFlagValue '= "") {
        Do $System.Process.SetZEOF(tOldSystemFlagValue)
    }
     
    // Cleanup: device
    If tFileOpen {
        Close pFile
        // If pFile is the current device, the CLOSE command switches $io back to the process's default device,
        // which might not be the same as the value of $io was when the method was called.
        // To be extra sure:
        Use tDevice
    }
     
    // Cleanup: locks
    If tMyGlobalLocked {
        Lock -^MyGlobal(42)
    }
     
    // Cleanup: transactions
    // Roll back one level at a time up to our starting transaction level.
    While $TLevel > tInitialTLevel {
        TROLLBACK 1
    }
     
    // } // end "finally"
    Quit tSC
}

Nota: en este enfoque, es fundamental que se utilice “Quit” y no “Return” en el bloque “Try” ...; “Return” haría un bypass del cleanup.

Patrón recomendado: Objetos Registrados y Destructores {#RobustErrorHandlingandCleanupinObjectScript-RecommendedPattern:RegisteredObjectsandDestructors}

A veces, el código de depuración puede complicarse. En estos casos, quizás tenga sentido facilitar la reutilización del código de depuración integrándolo en un registered object. El estado del sistema es inicializado cuando se inicializa el objeto o cuando se llama a los métodos del objeto que cambian el estado, y regresa a su valor original cuando el objeto ya no está al alcance. Mira este sencillo ejemplo, que administra las transacciones, el namespace actual y el estado de $System.Process.SetZEOF:

/// When an instance of this class goes out of scope, the namespace, transaction level, and value of $System.Process.SetZEOF() that were present when it was created are restored.
Class DC.Demo.ScopeManager Extends %RegisteredObject
{
 
Property InitialNamespace As %String [ InitialExpression = {$Namespace} ];
 
Property InitialTransactionLevel As %String [ InitialExpression = {$TLevel} ];
 
 
Property ZEOFSetting As %Boolean [ InitialExpression = {$System.Process.SetZEOF()} ];
 
 
Method SetZEOF(pValue As %Boolean)
{
    Set ..ZEOFSetting = $System.Process.SetZEOF(.pValue)
}
 
Method %OnClose() As %Status [ Private, ServerOnly = 1 ]
{
    Set tSC = $$$OK
     
    Try {
        Set $Namespace = ..InitialNamespace
    } Catch e {
        Set tSC = $$$ADDSC(tSC,e.AsStatus())
    }
     
    Try {
        Do $System.Process.SetZEOF(..ZEOFSetting)
    } Catch e {
        Set tSC = $$$ADDSC(tSC,e.AsStatus())
    }
     
    Try {
        While $TLevel > ..InitialTransactionLevel {
            TROLLBACK 1
        }
    } Catch e {
        Set tSC = $$$ADDSC(tSC,e.AsStatus())
    }
     
    Quit tSC
}
 
}

La siguiente clase demuestra cómo la clase registrada anteriormente podría utilizarse para simplificar la depuración cuando finalice el método:

Class DC.Demo.Driver
{
 
ClassMethod Run()
{
    For tArgument = "good","bad" {
        Do ..LogState(tArgument,"before")
        Do ..DemoRobustMethod(tArgument)
        Do ..LogState(tArgument,"after")
    }
}
 
ClassMethod LogState(pArgument As %String, pWhen As %String)
{
    Write !,pWhen," calling DemoRobustMethod("_$$$QUOTE(pArgument)_"):"
    Write !,$c(9),"$Namespace=",$Namespace
    Write !,$c(9),"$TLevel=",$TLevel
    Write !,$c(9),"$System.Process.SetZEOF()=",$System.Process.SetZEOF()
}
 
ClassMethod DemoRobustMethod(pArgument As %String)
{
    Set tScopeManager = ##class(DC.Demo.ScopeManager).%New()
     
    Set $Namespace = "%SYS"
    TSTART
    Do tScopeManager.SetZEOF(1)
    If (pArgument = "bad") {
        // Normally, this would be a big problem. In this case, because of tScopeManager, it isn't.
        Quit
    }
    TCOMMIT
}

}
1
1 144
Artículo Mathew Lambert · ene 26, 2021 2m read

En los recientes trabajos de benchmarking a gran escala, observamos un tiempo excesivo de uso del CPU %sys que afectó negativamente en la escalabilidad de la aplicación.

Problema

Encontramos que gran parte del tiempo se pasó llamando a la llamada localtime() del sistema, debido a que la variable de entorno TZ no estaba configurada. Se creó una rutina de prueba sencilla para confirmar la observación, y las diferencias entre el tiempo transcurrido y los recursos que necesitó la CPU con la variable TZ vs. cuando TZ no estaba establecida, fueron extraordinarios. Se descubrió que el uso hereditario de las llamadas stat() de sistema hacia /etc/local_time desde localtime() es muy costoso cuando la variable TZ no está establecida.

Recomendación

InterSystems recomienda encarecidamente que se confirme en cualquier sistema que tenga Linux instalado, ya sea en x86 o Linux on Power, que la variable de entorno TZ está configurada adecuadamente, para obtener un rendimiento óptimo. Para más información, consulta: "man tzset".

La versión de prueba de Caché 2016.1 contiene optimizaciones relacionadas con las funciones de fecha y hora, y las pruebas iniciales indican importantes mejoras. A continuación, se muestran ejemplos de salidas, resultado de probar las nuevas llamadas a funciones internas en un bucle con Linux on Power, donde en ambos casos TZ *no* se establece:

Antes de Caché 2016.1 FT:

real	0m22.60suser	0m1.64ssys	0m20.89s

Con Caché 2016.1 FT:

real	0m0.40s
user	0m0.37s
sys	0m0.00s

Publica tus comentarios sobre cualquier experiencia que hayas tenido con tu aplicación, relacionada con la variable de entorno TZ, y también sobre el impacto que la versión de prueba de Caché 2016.1 tiene con o sin la variable TZ definida.

0
0 209
Pregunta Mathew Lambert · ene 13, 2021

¿Alguien sabe si hay una manera fácil de saber si la ejecución del comando $ System.SQL.PurgeForTable ha ido bien / mal?

La documentación describe que se devuelve un string, pero de hecho hay un bonito  Quit ""  en el código.

¿Quizás dentro de PurgeForTable ^% apiSQL hay alguna variable de proceso establecida cuando va bien / mal?

¡Muchas gracias!

3
0 157
Artículo Mathew Lambert · dic 18, 2020 2m read

Hace poco, tuve que generar una especificación en Swagger a partir de clases persistentes y en serie, así que ahora publico el código (no está completo, aún queda resolver las especificaciones de la aplicación, pero es un comienzo).

El código está disponible aquí.

Supongamos que tenemos estas clases:

 
Clases

Se puede generar automáticamente esta definición de Swagger a partir de ellas:

 REST.Test.Person:
   type: "object"
   properties:
     Age:
       type: "integer"
     DOB:
       type: "string"
     FavoriteColors:
       type: "array"
       items:
         type: "string"
     FavoriteNumbers:
       type: "object"
     Home:
       $ref: "#/definitions/REST.Test.Address"
     Name:
       type: "string"
     Office:
       $ref: "#/definitions/REST.Test.Address"
     SSN:
       type: "string"
     Spouse:
       $ref: "#/definitions/REST.Test.Person"
 REST.Test.Address:
   type: "object"
   properties:
     City:
       type: "string"
     State:
       type: "string"
     Street:
       type: "string"
     Zip:
       type: "string"

Método principal: Utils.YAML:GenerateClasses

Prueba de ejecución: do ##class(Utils.YAML).Test()

0
0 324
Artículo Mathew Lambert · dic 17, 2020 3m read

Uno de los principales beneficios de ObjectScript es la velocidad y eficiencia que ofrece a un desarrollador experimentado. Veamos un ejemplo de todos los beneficios que se pueden obtener con ObjectScript.

Imagina que tienes una clase que almacena nombres de sus usuarios. Llamaremos a la clase **Data.User**, y le daremos las mismas propiedades que tiene una cadena _Name_. A continuación, necesitaremos un método para crear un nuevo usuario o actualizar uno ya existente. Un enfoque inexperto y simplista sería muy similar al siguiente ejemplo:

0
0 127
Artículo Mathew Lambert · nov 25, 2020 17m read

Este artículo fue creado como efecto secundario de las preparaciones para un conjunto más extendido de artículos sobre la simple, pero útil, implementación de MapReduce en Caché. Estaba buscando una forma relativamente fácil de pasar argumentos a (potencialmente) múltiples objetivos mediante mecanismos de invocación remota. Y tras varios intentos, me di cuenta de que en el ObjectScript de Caché contamos con mecanismos muy potentes que podrían ser de utilidad para esto: dynamix dispatching para métodos y propiedades.

0
1 153
Artículo Mathew Lambert · oct 14, 2020 1m read

Esto lo escribo más para ayudar a mi memoria que para otra cosa. Pensé en compartirlo, porque a menudo aparece en los comentarios, pero no está en la documentación de InterSystems

Hay una utilidad increíble que se llama ^REDEBUG, que aumenta el nivel de información que se registra en mgr\cconsole.log. 

Para activarla:

a) inicia el terminal/login

b) zn "%SYS"

c) do ^REDEBUG

d) cambia el nivel de registro a FFFFFFFF

0
0 154
Artículo Mathew Lambert · jul 31, 2020 2m read

¡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

0
0 152
Artículo Mathew Lambert · jul 24, 2020 2m read

¡Hola desarrolladores!

Estoy seguro de que la mayoría de vosotros ya conoceis la posibilidad de usar GZIP en los productos de InterSystems. Pero el problema es que GZIP trabaja con un único archivo o flujo de datos, y no admite carpetas. Al trabajar con sistemas Unix, es posible solucionarlo con la herramienta de compresión tar, incluida en cada sistema Linux de forma predeterminada. Pero ¿qué hacer si debemos trabajar también en Windows, que no lo incluye?

Me alegra poder enseñaros el nuevo proyecto, isc-tar, que os ayudará a no preocuparos del sistema operativo y os permitirá gestionar los archivos tar en cualquier sitio.

0
0 109
Pregunta Mathew Lambert · jul 20, 2020
Usamos el método de encriptacion de contrseñas mencionado y necesitamos saber si la implementación es acorde con las recomendaciones NIST / FIPS https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
 
Leyendo la documentación no me queda 100% claro ya que dice "(See RSA Laboratories Public-Key Cryptography Standards #5 and Federal Information Processing Standards Publications 180-4 and 198-1 for more information.)"
1
0 736
Artículo Mathew Lambert · jul 15, 2020 4m read

¡Hola desarrolladores!

Aquí podéis ver el anuncio del proyecto isc-tar de @Dmitriy Maslennikov. En ocasiones, la historia de porque se ha llegado a un resultado es igual o más interesante que el resultado: cómo se construyó, cómo funciona y qué sucede en torno al proyecto. Esta es la historia:

  • Cómo desarrollar este proyecto
  • Cómo probarlo
  • Cómo lanzar nuevas versiones para publicar
  • Cómo automatizar todo lo anterior
  • Integración continua

Os hablaré de todo eso.

0
0 134
Artículo Mathew Lambert · jul 7, 2020 6m read

Introducción

Si gestionas múltiples instancias de Caché en varios servidores, puede que quieras ejecutar código arbitrario de una instancia de Caché en otra. Los administradores de sistemas y especialistas de soporte técnico también podrían querer ejecutar un código arbitrario en servidores Caché remotos. Para satisfacer estas necesidades, he desarrollado una herramienta especial llamadaRCE.
En este artículo, analizaremos cuáles son las formas más comunes de resolver tareas similares y cómo RCE (Remote Code Execution) puede ser útil.
0
0 439
Artículo Mathew Lambert · jun 25, 2020 3m read

El propósito de este artículo es reforzar el perfil de un procedimiento muy potente que ha estado disponible desde hace mucho tiempo para nosotros, y abrir un debate sobre las maneras en que puede utilizarse (o explotarse).

Puedes leer más información sobre el el mecanismo aquí. En resumen, cuando definas una clase puedes utilizar la palabra clave Projection para referirte a una o más clases de proyección. Una clase de proyección permite implementar métodos que se llaman desde puntos clave en el ciclo de vida de tu clase.

0
0 507
Artículo Mathew Lambert · jun 15, 2020 7m read

Esta es una guía para principiantes, para el desarrollo de servicios web RESTful con Ensemble.

Contexto

Antes de comenzar a leer esta breve introducción, lee la documentación de Ensemble, prestando especial atención al capítulo sobre creación de servicios y clientes REST con Ensemble (“Creating REST services and clients with Ensemble”)

El enfoque de esa documentación creo que es indiscutiblemente la forma más rápida y simple de crear servicios RESTful. Como principiante, leí la documentación y me quedaron varias dudas. Este breve artículo enumera esas preguntas, junto con mis humildes respuestas

0
0 400
Artículo Mathew Lambert · mayo 19, 2020 2m read

Los WebSockets, como tecnología de comunicación, están ganando una importancia cada vez mayor.
En el namespace SAMPLES puedes encontrar un buen ejemplo para ejecutar un WebSocket Server.
También hay un útil ejemplo para un Browser Client, pero sigue estando en el navegador.

La cuestión es:
¿Cómo consumir la salida de un WebSocket Server en tu aplicación?

0
0 147
Artículo Mathew Lambert · mayo 18, 2020 2m read

Los WebSockets, como tecnología de comunicación, están ganando una importancia cada vez mayor.
En el namespace SAMPLES puedes encontrar un buen ejemplo para ejecutar un WebSocket Server.
También hay un ejemplo útil para un Browser Client en el cual JavaScript hace la mayor parte del trabajo. 

La cuestión es:
¿Cómo consumir la salida de un WebSocket Server en tu aplicación?

0
0 177
Artículo Mathew Lambert · mayo 7, 2020 4m read

¡Hola Comunidad!

Hace un par de días, un cliente me comunicó su intención de mejorar su aplicación legacy existente, que usa Servicios Web SOAP, por lo que comparte la misma autorización con su nueva API de aplicación basada en REST. Como su nueva aplicación usa OAuth2, el desafío estaba claro: cómo pasar un token de acceso con una solicitud SOAP al servidor.

Tras Googlear un poco, descubrí que una de las formas posibles de hacer esto era agregar un elemento de encabezado adicional al SOAP envelope y luego asegurarse de que la implementación del Webservice haga lo necesario para validar el token de acceso.

0
0 1351
Pregunta Mathew Lambert · abr 28, 2020

He estado leyendo la guía de documentación para 2018.1 sobre los planes de consultas congeladas varias veces en los últimos días ( enlace ) y hay una respuesta que no encuentro directamente.

Mi forma de actualización on premise es:

  • Actualizar la versión de HealthShare
  • Descongelar todos los planes de consulta de mi namespace
  • Purgado de todas las queries
  • Sobreescribo la base de datos que contiene la lógica (tanto la lógica antigua como la nueva contienen consultas estáticas compiladas con la versión HS de destino y consultas dinámicas)
1
0 134
Artículo Mathew Lambert · abr 16, 2020 10m read

¡Hola Comunidad!

Descubrí el Desarrollo Basado en Pruebas (TDD) hace casi 9 años y me enamoré del concepto inmediatamente.

Hoy se ha vuelto muy popular pero, desafortunadamente, muchas empresas no lo usan. Es más, muchos desarrolladores, sobre todo principiantes, ni siquiera saben exactamente qué es ni como usarlo.

Resumen

Mi objetivo con este artículo es mostrar cómo usar TDD con %UnitTest. Mostraré mi flujo de trabajo y explicaré cómo usar cosFaker, un proyecto de @Henry Pereira Pereira, usando Caché y que hace poco lo subió a OpenExchange.

Así que... ¡preparaos que allá vamos!

¿Qué es TDD?

0
0 271
Artículo Mathew Lambert · mar 25, 2020 6m read

¡Hola Comunidad!

Creo que hoy en día todo el mundo guarda el código fuente de sus proyectos en repositorios como Github, GitLab, bitbucket, etc. Lo mismo sucede con proyectos de InterSystems IRIS, se pueden ver algunos ejemplos en Open Exchange.

¿Qué hacemos cada vez que empezamos o continuamos nuestro trabajo con un repositorio en particular con la plataforma de datos InterSystems?

1
1 909
Artículo Mathew Lambert · mar 20, 2020 2m read

Hola a tod@s,

En recientes conversaciones, respuestas y comentarios, la redirección IO (con el famoso código) se veía como la solución indiscutible. Sin duda, es una solución potente y no solo permite registrar la salida, sino también hacer captura de teclas en la entrada.

Pero creo que para simplemente generar un registro (log), supone un sobretrabajo. Si solo necesitas obtener un texto o descargar sus variables u objetos, entonces SPOOL debería ser suficiente.

0
0 118
Artículo Mathew Lambert · mar 6, 2020 3m read

¡Hola Comunidad!

Esta publicación es para presentarles uno de los primeros proyectos en COS de @Henry Pereira . Lo creó cuando empezó a aprender el lenguaje y lo sigue mejorando hasta hoy.

El CosFaker (aquí en Github) es una biblioteca de COS pura, para generar datos falsos.

cosFaker vs Utils de populación

¿Por qué usar cosFaker si Caché tiene la utilidad de popular datos?

1
1 156