#Operación empresarial

0 Seguidores · 22 Publicaciones

InterSystems Ensemble proporciona clases especializadas en operaciones empresariales que utilizan adaptadores de salida específicos para comunicarse con sistemas externos y la interfaz de usuario (UI).

Documentación.

Artículo Jose-Tomas Salvador · mayo 19, 2025 3m read

Uno de los desafíos al crear un mensaje DICOM es cómo poner los datos en el lugar correcto. Parte de ello es insertar los datos en las etiquetas específicas de DICOM, mientras que la otra parte es insertar datos binarios como una imagen. En este artículo explicaré ambos.

Para crear un mensaje DICOM, podéis usar la clase EnsLib.DICOM.File (para crear un archivo DICOM) o la clase EnsLib.DICOM.Document (para crear un mensaje que se pueda enviar directamente a PACS). En ambos casos, el método SetValueAt os permitirá añadir vuestros datos a las etiquetas DICOM.

0
0 52
Artículo Jose-Tomas Salvador · ene 22, 2025 1m read

En vuestra Producción de Interoperabilidad, siempre podíais tener una Business Operation (BO) que fuera un cliente HTTP y que utilizara OAuth 2.0 para la autenticación. Sin embargo, teníais que personalizar la BO para esta metodología de autenticación. Desde la versión 2024.3, que se lanzó recientemente, hay una nueva capacidad que proporciona nuevos ajustes para gestionar esto de forma más sencilla.

0
0 56
Artículo Ricardo Paiva · ene 8, 2025 4m read

Link de Git: https://github.com/ecelg/InterSystems-IRIS-as-a-Spotify-REST-client

Recientemente se me ocurrió una idea: ¿cómo puedo poner mi lista de reproducción en IRIS? 🧐

Al mismo tiempo, me dijeron que debía pagar mi suscripción de Spotify 💸💸... oooh... ¿y si obtengo algunos datos de la API de Spotify? Así que empecé a investigar sobre eso.

Como en la mayoría de los desarrollos, comencemos con la documentación de la API: https://developer.spotify.com/documentation/web-api

Para obtener los datos, se requiere solicitar un token de acceso en la URL del endpoint de token. 🧐

0
0 89
Artículo Alberto Fuentes · sep 23, 2024 8m read

¡Hola, desarrolladores!

En esta serie de artículos hemos hablado del framework iris-datapipe, de cómo nos ayuda a crear "pipes" de datos para la ingesta y procesamiento, y de cómo instalarlo. Vamos a profundizar en cómo implementar uno de esos "pipes" paso a paso.

Si llegaste directamente a este artículo, te recomiendo revisar los anteriores y recordar que iris-datapipe incluye un QuickStart para que puedas explorar sus funcionalidades rápidamente 👌.

El ejemplo que abordaremos está incluido en el QuickStart, por lo que puedes utilizarlo como referencia.

Definir un nuevo Pipe

Comienza por definir un nuevo Pipe en la interfaz gráfica. Básicamente, solo se trata de asignar un código y una descripción.

Opcionalmente, puedes especificar un recurso de seguridad de IRIS que se requerirá para poder operar con ese Pipe (esto es útil si necesitas crear pipes que solo sean accesibles a determinados usuarios).

En el ejemplo, definimos un pipe llamado REST-API, que se encargará de procesar datos recibidos desde una API REST, donde llegarán datos sobre personas.

image

Procesamiento de los datos

Para procesar los datos utilizando un "pipe", necesitamos seguir los siguientes pasos:

1) Definir un modelo DataPipe

Debemos definir un modelo para los datos que queremos procesar.

Un modelo no es más que una clase que hereda o extiende de DataPipe.Model.cls donde tendrás que implementar algunos métodos.

Tu modelo debe implementar:

  • Cómo serializar/deserializar tus datos (por ejemplo, usando XML o JSON).
  • Cómo normalizar y validar tus datos.
  • Y finalmente, qué operación quieres ejecutar sobre tus datos una vez están normalizados y validados.

En mi ejemplo, el modelo que utilizaré será DataPipe.Test.REST.Models.Person.cls.

Dado que el modelo es una clase convencional en InterSystems IRIS, puedes añadir herencia u otro comportamiento que necesites. En mi caso, heredo de DataPipe.Test.REST.Msg.PersonData, donde tengo definidas las propiedades que me interesa tratar.

El modelo DataPipe.Test.REST.Models.Person.cls tiene diferentes métodos:

  • Serialize, Deserialize: Se utilizan para indicar cómo serializar/deserializar el modelo. En este caso, utilizo JSON.
  • Normalize: Especifica cómo quiero normalizar el modelo. En mi caso, solo quiero llamar a una transformación de datos.
/// Normalize model
Method Normalize(Output obj As DataPipe.Model) As %Status
{
    set ret = $$$OK
    try {
        // call normalizaton data transform
        set sc = $classmethod("DataPipe.Test.REST.DT.PersonNormalize", "Transform", $this, .obj)
        $$$ThrowOnError(sc)

    } catch ex {
        set ret = ex.AsStatus()
    }
    quit ret
}
  • Validate: Indica cómo quiero validar si mi modelo es correcto o no. Puedo añadir "warnings" también. Puedes implementar lo que necesites:
/// Validate model
Method Validate(Output errorList As %List) As %Status
{
    #define AddError(%list, %code, %desc) set error = ##class(DataPipe.Data.ErrorInfo).%New() set error.Code=%code set error.Desc=%desc do %list.Insert(error)
	
    set ret = $$$OK
    try {
        set errorList = ##class(%ListOfObjects).%New()

        // date of birth
        if ..DOB="" { 
            $$$AddError(errorList, "V001", "DOB required")
        } else {
            set yearDOB = $extract($zdate(..DOB,8),1,4)
            if (yearDOB < 1930) $$$AddError(errorList, "V002", "DOB must be greater than 1930")
            if (yearDOB < 1983) $$$AddError(errorList, "W083", "Warning! Older than 1983")
        }

        // model is invalid if errors (not warnings) found
        for i=1:1:errorList.Count() {
            set error = errorList.GetAt(i)
            set errorCode = error.Code

            // in this sample model, all warnings start with "W"
            if errorCode'["W" {
                $$$ThrowStatus($$$ERROR($$$GeneralError, "Invalid"))
            }
		}
       
    } catch ex {
        set ret = ex.AsStatus()
    }

    quit ret
}
  • GetOperation: Indica qué Business Operation quiero que ejecute la operación sobre los datos (lo entenderás mejor cuando comentemos los componentes de interoperabilidad).
/// Return the Business Operation name that will run the operation with the model
/// Each Business Operation can be used to hold different queues
Method GetOperation() As %Status
{
    quit "Person Operation"
}
  • RunOperation: Esta es la operación que quiero ejecutar sobre mis datos. Puedo guardarlos en la base de datos o llamar a otro componente de interoperabilidad para continuar el procesamiento más adelante. En mi ejemplo, los guardo en una global y también los envío a otro Business Process.
/// Run final operation with the model
/// This method can be used to persit data from the model to an operational data store
Method RunOperation(Output errorList As %List, Output log As %Stream.Object, bOperation As Ens.BusinessOperation = "", Output delayedProcessing As %Boolean = 0) As %Status
{
    #define AddError(%list, %code, %desc) set error = ##class(DataPipe.Data.ErrorInfo).%New() set error.Code=%code set error.Desc=%desc do %list.Insert(error)
    #define AddLog(%log, %msg) do %log.WriteLine("["_$zdt($h,3)_"] "_%msg)
	
    set errorList = ##class(%ListOfObjects).%New()
    set log = ##class(%Stream.GlobalCharacter).%New()

    set ret = $$$OK
    try {
        TSTART
        $$$AddLog(log, "Transaction Started")

        // simulate an operation error
        if ##class(Ens.Util.FunctionSet).In(..Name, ##class(DataPipe.Test.HL7.Helper).OperationErrorNames()) {
            $$$ThrowStatus($$$ERROR($$$GeneralError, "Simulated Operation Error"))
        }

        // store serialized model
        $$$ThrowOnError(..Serialize(.stream))
        set ^zDataPipe($i(^zDataPipe)) = stream.Read()
        $$$AddLog(log, "Model Stored in ^zDataPipe("_$get(^zDataPipe)_")")

        TCOMMIT
        $$$AddLog(log, "Transaction Commited")

        // you can send messages to other production components (while you are not on an open transaction)
        // you can use this feature to continue processing the record in other component (delayed processing)
        set delayedProcessing = 1
        if $isobject(bOperation) {
            set req = bOperation.OperRequest
            $$$ThrowOnError(bOperation.SendRequestAsync("REST Delayed Oper Update", req))
        }

    } catch ex {
        TROLLBACK 
        $$$AddLog(log, "Rollback!")

        set ret = ex.AsStatus()
        $$$AddLog(log, "Error catched: "_$system.Status.GetOneStatusText(ret))

        // include exception errors into errorList
        do $system.Status.DecomposeStatus(ret, .errors)
		for i=1:1:errors {
			$$$AddError(errorList, "Exception", errors(i))
		}
    }
    quit ret
}

2) Añadir componentes de interoperabilidad

Después de definir tu modelo, necesitas configurar una producción de interoperabilidad usando componentes de DataPipe. iris-datapipe incluye componentes preconstruidos que pueden operar con un modelo DataPipe como el que hemos definido anteriormente.

El único proceso que debes implementar es el proceso de ingestión.

2.1) Crear un Proceso de Ingestión

Necesitas crear un nuevo Business Process que utilice como contexto DataPipe.Ingestion.BP.IngestionManagerContext.

Este proceso recibirá la entrada de datos que decidas (en este ejemplo, el mensaje que envía una API REST que actúa como Business Service) y debe implementar:

Identificación de los datos que procesas (InboxAttributes):

  • Debes identificar el registro que estás procesando y proporcionar los InboxAttributes.
  • El "pipe" al que pertenece este registro debe indicarse en este momento.
  • Estos atributos describirán el registro que estás tratando y luego se utilizarán para la búsqueda desde la interfaz gráfica.
  • Para realizar lo anterior, puedes utilizar transformaciones de datos, código, ¡lo que necesites!

Convertir los datos de entrada en el modelo de datos que definiste previamente:

  • Debes utilizar transformaciones, código o lo que prefieras para transformar la información de entrada al modelo de datos que has desarrollado previamente.

image

2.2) Añadir el resto de componentes

El resto de los componentes de interoperabilidad son proporcionados por iris-datapipe y ya están preconstruidos. Estos componentes llamarán a los distintos métodos implementados en tu modelo.

En general, necesitarás añadir a la producción, por cada "pipe" diferente que quieras implementar:

Aquí tienes la producción de ejemplo que se utiliza en el QuickStart.

Con todo esto, cuando comiencen a llegar datos y se procesen, podrás verlos directamente desde la interfaz gráfica.

image

¡Espero que os sea útil!

0
0 74
Artículo Jose-Tomas Salvador · ago 27, 2024 4m read

Acceder a un almacenamiento en la nube de Azure para cargar/descargar blobs es bastante fácil usando los métodos API designados de la clase %Net.Cloud.Storage.Client, o usando los adaptadores de entrada/salida EnsLib.CloudStorage.*

Tened en cuenta que necesitaréis tener el Servidor de Lenguaje Externo %JavaServer en funcionamiento para usar tanto la API de almacenamiento en la nube como los adaptadores, ya que ambos usan el marco PEX usando el Servidor Java.

He aquí un breve resumen:

El acceso a Azure Blob Storage se realiza mediante una cadena de conexión similar a esta:

0
0 121
Artículo Alberto Fuentes · ago 8, 2024 5m read

Hacía mucho tiempo que no escribía un post de actualización en IoP (Interoperabilidad en Python).

image

¿Qué hay de nuevo desde el lanzamiento de la interfaz de línea de comandos de IoP?

Dos nuevas grandes características se han añadido a IoP:

  • Rebranding: el módulo grongier.pex fue renombrado a iop para reflejar el nuevo nombre del proyecto.
  • Soporte asíncrono**: IoP ahora soporta funciones asíncronas y corrutinas.

Rebranding

El módulo grongier.pex ha sido renombrado a iop para reflejar el nuevo nombre del proyecto.

El módulo grongier.pex sigue disponible para compatibilidad con versiones anteriores, pero se eliminará en el futuro.

Soporte Async

IoP soporta llamadas asíncronas desde hace mucho tiempo, pero no era posible utilizar funciones asíncronas y corrutinas directamente en IoP.

Antes de saltar a esta nueva característica, voy a explicar cómo funcionan las llamadas async en InterSystems IRIS y presentar dos ejemplos de cómo utilizar las llamadas async IoP.

Llamadas asíncronas heredadas

Veamos cómo funcionan las llamadas async legacy:

from iop import BusinessProcess
from msg import MyMessage


class MyBP(BusinessProcess):

    def on_message(self, request):
        msg_one = MyMessage(message="Message1")
        msg_two = MyMessage(message="Message2")

        self.send_request_async("Python.MyBO", msg_one,completion_key="1")
        self.send_request_async("Python.MyBO", msg_two,completion_key="2")

    def on_response(self, request, response, call_request, call_response, completion_key):
        if completion_key == "1":
            self.response_one = call_response
        elif completion_key == "2":
            self.response_two = call_response

    def on_complete(self, request, response):
        self.log_info(f"Received response one: {self.response_one.message}")
        self.log_info(f"Received response two: {self.response_two.message}")

Básicamente funcionan de la misma manera que las llamadas asíncronas en IRIS. El método send_request_async envía una petición a una Operación de Negocio y el método on_response es llamado cuando se recibe la respuesta.

Podéis distinguir las respuestas por el parámetro completion_key.

Enviar múltiples peticiones de sincronización

No es exactamente una nueva característica, pero vale la pena mencionar que se pueden enviar múltiples solicitudes de sincronización en paralelo:

from iop import BusinessProcess
from msg import MyMessage


class MyMultiBP(BusinessProcess):

    def on_message(self, request):
        msg_one = MyMessage(message="Message1")
        msg_two = MyMessage(message="Message2")

        tuple_responses = self.send_multi_request_sync([("Python.MyMultiBO", msg_one),
                                                        ("Python.MyMultiBO", msg_two)])

        self.log_info("All requests have been processed")
        for target,request,response,status in tuple_responses:
            self.log_info(f"Received response: {response.message}")

Aquí estamos enviando dos peticiones a la misma Operación de Negocio en paralelo.

La respuesta es una tupla con el objetivo, la petición, la respuesta y el estado de cada llamada.

Es realmente útil cuando necesitáis enviar múltiples peticiones y no te importa el orden de las respuestas.

Funciones asíncronas y corrutinas

Ahora vamos a ver cómo utilizar funciones asíncronas y corrutinas en IoP:

import asyncio

from iop import BusinessProcess
from msg import MyMessage


class MyAsyncNGBP(BusinessProcess):

    def on_message(self, request):

        results = asyncio.run(self.await_response(request))

        for result in results:
            print(f"Received response: {result.message}")

    async def await_response(self, request):
        msg_one = MyMessage(message="Message1")
        msg_two = MyMessage(message="Message2")

        # use asyncio.gather to send multiple requests asynchronously
        # using the send_request_async_ng method
        tasks = [self.send_request_async_ng("Python.MyAsyncNGBO", msg_one),
                 self.send_request_async_ng("Python.MyAsyncNGBO", msg_two)]

        return await asyncio.gather(*tasks)

En este ejemplo, estamos enviando múltiples peticiones a la misma Operación de Negocio en paralelo usando el método send_request_async_ng.

Si habéis leído atentamente este post hasta este punto, comentad «Boomerang». Esto puede ser un simple detalle para vosotros, pero para mi significa mucho. Gracias.

El método await_response es una corrutina que envía múltiples peticiones y espera a recibir todas las respuestas. Gracias a la función asyncio.gather, podemos esperar a que se reciban todas las respuestas en paralelo.

Las ventajas de utilizar funciones asíncronas y corrutinas son:

  • Mejor rendimiento: podéis enviar múltiples peticiones en paralelo.
  • Más fácil de leer y mantener: podéis usar la palabra clave await para esperar las respuestas.
  • Más flexibilidad: podéis utilizar el módulo asyncio para crear flujos de trabajo complejos.
  • Más control: podéis utilizar el módulo asyncio para manejar excepciones y tiempos de espera.

Conclusión

¿Cuáles son las diferencias entre send_request_async, send_multi_request_sync y send_request_async_ng?

  • send_request_async: envía una petición a una Operación de Negocio y espera la respuesta **si** se implementa el método on_responsey se utiliza el parámetrocompletion_key`.
    • ventaja: podéis utilizar las llamadas async de la forma habitual.
    • desventaja: puede ser difícil de mantener si necesitáis enviar múltiples peticiones en paralelo.
  • send_multi_request_sync`: envía múltiples peticiones a la misma Operación de Negocio en paralelo y espera a que se reciban todas las respuestas.
    • ventaja: es fácil de usar.
    • desventaja: se puede controlar el orden de las respuestas (es decir, la lista de respuestas no está ordenada).
  • send_request_async_ng`: envía múltiples peticiones a la misma Operación de Negocio en paralelo y espera a que se reciban todas las respuestas.
    • ventaja: se puede controlar el orden de las respuestas.
    • desventaja: es necesario utilizar funciones asíncronas y coroutines.

¡Feliz multithreading!

0
0 71
Artículo Jose-Tomas Salvador · jun 3, 2024 1m read

Añadid una credencial para iniciar sesión en la interfaz REST de FHIR - en este caso considerad sólo una autenticación básica

 

Añadid Registro de Servicios - en este caso considerad sólo una autenticación básica

- Configurad un servicio HTTP

- Introducid la ruta al servidor FHIR

- Introducid la URL del servicio FHIR

- Usad la credencial añadida previamente 

 

Añadid una "HS.FHIRServer.Interop.HTTPOperation"

Elegid el Nombre del Servicio

Probad el cliente FHIR

Rastread/seguid el resultado de la prueba

0
1 121
Artículo Kurro Lopez · mayo 21, 2024 10m read

Siguiendo con la serie de artículos sobre gestión de archivos de voz, vamos a ver cómo podemos convertir texto en audio y recibir el archivo con la voz elegida.
También exploraremos cómo un servicio de OpenAI puede ayudarnos a analizar un texto y determinar el estado de ánimo expresado en él.
Analicemos cómo puedes crear tu propio archivo de voz y cómo puede "leer" tus sentimientos.

0
0 116
Artículo Kurro Lopez · mar 3, 2024 5m read

Hola comunidad,

El objetivo de este artículo es explicar como crear mensajes entre IRIS y Microsoft Teams.

En mi empresa, tenermos que monitorear mensajes de error, y usamos la clase Ens.Alerts para redireccionar esos mensajes a través de un Business Operation que envía un email.
El problema está en que enviamos esos errores a una cuenta de soporte donde habían muchos emails. Queremos algo específico para un específico equipo.

Por lo que investigamos como hacer que esos mensajes lleguén al equpo de desarrollo directamente y ellos pordrían tener, en tiempo real, una notificación de un error en producción. 
En nuestra empresa usamos Microsoft Teams como herramienta corportaiva, por lo que nos preguntamos: ¿Cómo podemos hacer que esos mensajes lleguen al equipo de desarrollo de IRIS?

4
0 416
Artículo Kurro Lopez · dic 8, 2023 9m read

 

La inteligencia artificial no se limita solo a generar imágenes a través de texto con instrucciones o crear narrativas con instrucciones sencillas.

También puedes hacer variaciones de una imagen o incluir un fondo especial a una ya existente.

Adicionalmente, podrás obtener la transcripción del audio sin importar su idioma y la velocidad del hablante.

Por tanto, analicemos cómo funciona la gestión de archivos.

0
0 194
Artículo Kurro Lopez · jun 19, 2023 9m read

 

Como todos ya conocéis, el mundo de la inteligencia artificial ya está aquí y todos quieren usarlo para su beneficio.

Hay muchas plataformas que ofertan servicios de inteligencia artificial de forma gratuita, por suscripción o privadas, pero la que mas “ruido” ha hecho en el mundo de la informática es Open AI, sobre todo por sus más famosos servicios: ChatGPT y DALL-E

<--break->¿Qué es Open AI?

1
0 407
Artículo Eduard Lebedyuk · sep 26, 2022 2m read

A veces necesitamos depurar una Business Operation (BO). El registro y seguimiento (trazas) funcionan, pero a veces quieres trabajar con una BO en tu sesión en el terminal local.

Así es como puedes hacerlo en cualquier sistema operativo.

Windows tiene una estupenda herramienta para depurar Business Operations - en modo Foreground. En ese modo, Windows ejecuta un terminal local con un job para la operación.

0
0 131
Artículo Ricardo Paiva · jul 7, 2022 15m read

Si tuvieras la oportunidad de cambiar algo en el Visualizador de Mensajes de Interoperabilidad en IRIS, ¿qué harías?

Después de publicar el artículo Panel de Control "IRIS History Monitor", recibí algunos comentarios muy interesantes y varias peticiones. Una de ellas fue un Visualizador de Mensajes mejorado.
 
Si aún no lo has hecho, echa un vistazo al proyecto: merece la pena que le dediques un rato, y además ganó el 3er premio (Bronce) a Los mejores desarrolladores y aplicaciones de InterSystems Open Exchange en 2019.
 
Empecé a pensar algunas ideas sobre lo que me gustaría incluir en el "nuevo" Visualizador de Mensajes pero ¿cómo podría mostrar estos recursos de la forma más rápida y sencilla?
0
0 174
Artículo Ricardo Paiva · jul 22, 2021 3m read

Posiblemente te hayas encontrado con esto y no supiste cómo evitarlo; o tal vez ni siquiera te diste cuenta...

Pero cuando se tienen componentes empresariales de la Producción de Interoperabilidad TCP (por ejemplo, un business service HL7), probablemente tienes (dependiendo de tu configuración de StayConnected) muchas entradas en el Registro de Eventos (Event log) de tipo Info. Cada entrada registraría una conexión o desconexión.

Por ejemplo:

0
0 156
Pregunta Kurro Lopez · mar 10, 2021

Hola a todos,

Tengo un error muy extraño cuando llamo a un servicio web SOAP como cliente.

He creado todos los objetos para invocar a este SOAP usando el complemento "Asistente SOAP" en Eclipse, ha creado todos los objetos (Response, Request, Clase WS de Business Operation, etc.).

Cuando llamo a este servicio, devuelve el siguiente mensaje de error:

ERROR #6243: La solicitud HTTP a SOAP WebService ha devuelto una respuesta con CONTENT-TYPE inesperado: text/html.

2
0 2671
Artículo Ricardo Paiva · mar 4, 2021 8m read

¡Hola Comunidad!

  Se acaba de lanzar OpenAPI-Client Gen, una aplicación para crear una producción cliente de interoperabilidad de IRIS a partir de la especificación Swagger 2.0.

  En vez de la herramienta existente ^%REST que crea una aplicación REST del lado del servidor, OpenAPI-Client Gen crea una plantilla completa de producción cliente de interoperabilidad REST.

La instalación se realiza por ZPM:

zpm "install openapi-client-gen"

  ¿Cómo generar la producción a partir de un documento Swagger?  

Hacerlo es muy sencillo.

Abre un terminal y ejecuta:

Set sc = ##class(dc.openapi.client.Spec).generateApp(<applicationName>, <Your Swagger 2.0 document>>)

  El primer argumento es el paquete objetivo donde se generarán las clases de la producción. El nombre del paquete debe ser un nombre válido e inexistente.
El segundo es el documento Swagger. Estos son los valores que se aceptan:

  1. Ruta del archivo.
  2. %DynamicObject.
  3. URL.
      La especificación debe estar en formato JSON.
      Si tu especificación utiliza el formato YAML puede convertirse fácilmente a JSON con algunas herramientas online, como onlineyamltools.com
      Ejemplo:
Set sc = ##class(dc.openapi.client.Spec).generateApp("petshop", "https://petstore.swagger.io:443/v2/swagger.json")
Write "Status : ", $SYSTEM.Status.GetOneErrorText(sc)

  Echa un vistazo al código generado, podemos ver muchas clases divididas en muchos subpaquetes:  

  • Business Service: petshop.bs
  • Business Operation: petshop.bo
  • Business Process: petshop.bp
  • REST Proxy application: petshop.rest
  • Ens.Request y Ens.Response: petshop.msg
  • Parsed input o Output object: petshop.model.Definition
  • Production configuration class: petshop.Production    

Business Operation class

Para cada servicio definido en el documento Swagger, hay un método relacionado llamado por <VERB><ServiceId>.

Análisis detallado de un método GETgetPetById simple generado  

/// Returns a single pet
Method GETgetPetById(pRequest As petshop.msg.getPetByIdRequest, pResponse As petshop.msg.GenericResponse) As %Status
{
    Set sc = $$$OK, pURL = "/v2/pet/{petId}"
    Set pHttpRequestIn = ..GetRequest(pRequest)
    Set pHttpRequestIn.ContentType = pRequest.consume
    Set pURL = $Replace(pURL, "{petId}", pRequest.pathpetId)
    $$$QuitOnError(..Adapter.SendFormDataArray(.pHttpResponse, "get", pHttpRequestIn , , , pURL))
    Set pResponse = ##class(petshop.msg.GenericResponse).%New()
    Set sc = ..genericProcessResponse(pRequest, pResponse, "GETgetPetById", sc, $Get(pHttpResponse),"petshop.msg.getPetByIdResponse")
    Return sc
}

 

  • En primer lugar, el objeto %Net.HttpRequest se crea por el método GetRequest. Si es necesario, no dudes en editarlo para agregar algunos encabezados.
  • En segundo lugar, el objeto HttpRequest se completa utilizando pRequest `petshop.msg.getPetByIdRequest' (subclase Ens.Request).
  • En tercer lugar, EnsLib.HTTP.OutboundAdapter se utiliza para enviar una solicitud http.
  • Y finalmente hay un proceso donde se origina una respuesta genérica por el método genericProcessResponse:
Method genericProcessResponse(pRequest As Ens.Request, pResponse As petshop.msg.GenericResponse, caller As %String, status As %Status, pHttpResponse As %Net.HttpResponse, parsedResponseClassName As %String) As %Status
{
    Set sc = $$$OK
    Set pResponse.operation = caller
    Set pResponse.operationStatusText = $SYSTEM.Status.GetOneErrorText(status)
    If $Isobject(pHttpResponse) {
        Set pResponse.httpStatusCode = pHttpResponse.StatusCode
        Do pResponse.body.CopyFrom(pHttpResponse.Data)
        Set key = ""
        For  {
            Set key = $Order(pHttpResponse.Headers(key),1 , headerValue)
            Quit:key=""
            Do pResponse.headers.SetAt(headerValue, key)
        }
        Set sc = ##class(petshop.Utils).processParsedResponse(pHttpResponse, parsedResponseClassName, caller, pRequest, pResponse)
    }
    Return sc
}

  Entonces, podemos analizar un método un poco más complejo POSTuploadFile

Method POSTuploadFile(pRequest As petshop.msg.uploadFileRequest, pResponse As petshop.msg.GenericResponse) As %Status
{
    Set sc = $$$OK, pURL = "/v2/pet/{petId}/uploadImage"
    Set pHttpRequestIn = ..GetRequest(pRequest)
    Set pHttpRequestIn.ContentType = pRequest.consume
    Set pURL = $Replace(pURL, "{petId}", pRequest.pathpetId)
    If pHttpRequestIn.ContentType = "multipart/form-data" {
        Set valueStream = ##class(%Stream.GlobalBinary).%New()
        Do:$Isobject(pRequest.formDataadditionalMetadata) valueStream.CopyFrom(pRequest.formDataadditionalMetadata)
        Do:'$Isobject(pRequest.formDataadditionalMetadata) valueStream.Write($Zcvt(pRequest.formDataadditionalMetadata,"I","UTF8"))
        Set:'$ISOBJECT($Get(mParts)) mParts = ##class(%Net.MIMEPart).%New()
        Set mimePart = ##class(%Net.MIMEPart).%New(valueStream)
        Do mimePart.SetHeader("Content-Disposition", "form-data; name=""additionalMetadata""; filename=""additionalMetadata""")
        Do mParts.Parts.Insert(mimePart)
    } Else { 
        Do pHttpRequestIn.InsertFormData("additionalMetadata", pRequest.formDataadditionalMetadata)
    }
    If pHttpRequestIn.ContentType = "multipart/form-data" {
        Set valueStream = ##class(%Stream.GlobalBinary).%New()
        Do:$Isobject(pRequest.formDatafile) valueStream.CopyFrom(pRequest.formDatafile)
        Do:'$Isobject(pRequest.formDatafile) valueStream.Write($Zcvt(pRequest.formDatafile,"I","UTF8"))
        Set:'$ISOBJECT($Get(mParts)) mParts = ##class(%Net.MIMEPart).%New()
        Set mimePart = ##class(%Net.MIMEPart).%New(valueStream)
        Do mimePart.SetHeader("Content-Disposition", "form-data; name=""file""; filename=""file""")
        Do mParts.Parts.Insert(mimePart)
    } Else { 
        Do pHttpRequestIn.InsertFormData("file", pRequest.formDatafile)
    }
    If $ISOBJECT($Get(mParts)) {
        Set mimeWriter = ##class(%Net.MIMEWriter).%New()
        Do mimeWriter.OutputToStream(.stream)
        Do mimeWriter.WriteMIMEBody(mParts)
        Set pHttpRequestIn.EntityBody = stream
        Set pHttpRequestIn.ContentType = "multipart/form-data; boundary=" _ mParts.Boundary
    }
    $$$QuitOnError(..Adapter.SendFormDataArray(.pHttpResponse, "post", pHttpRequestIn , , , pURL))
    Set pResponse = ##class(petshop.msg.GenericResponse).%New()
    Set sc = ..genericProcessResponse(pRequest, pResponse, "POSTuploadFile", sc, $Get(pHttpResponse),"petshop.msg.uploadFileResponse")
    Return sc
}

  Como puedes ver, es exactamente la misma lógica: GetRequest, completar %Net.HttpRequest, enviar solicitudes, procesar respuestas genéricas.
 

Proxy REST class

También se genera una Proxy REST application.
Esta clase REST utiliza un Projection para crear automáticamente la aplicación web relacionada (por ejemplo: "/petshoprest", consulta petshop.rest.REST y petshop.rest.Projection).   Este proxy REST crea el mensaje Ens.Request y lo envía hacia Business.Process.

Class petshop.rest.REST Extends %CSP.REST [ ProcedureBlock ]
{

Projection WebApp As petshop.rest.Projection;

...

ClassMethod POSTaddPet() As %Status
{
    Set ensRequest = ##class(petshop.msg.addPetRequest).%New()
    Set ensRequest.consume = %request.ContentType
    Set ensRequest.accept = $Get(%request.CgiEnvs("HTTP_ACCEPT"),"*/*")
    Set ensRequest.bodybody = ##class(petshop.model.Definition.Pet).%New()
    Do ensRequest.bodybody.%JSONImport(%request.Content)
    Return ##class(petshop.Utils).invokeHostAsync("petshop.bp.Process", ensRequest, "petshop.bs.ProxyService")
}

ClassMethod GETgetPetById(petId As %String) As %Status
{
    Set ensRequest = ##class(petshop.msg.getPetByIdRequest).%New()
    Set ensRequest.consume = %request.ContentType
    Set ensRequest.accept = $Get(%request.CgiEnvs("HTTP_ACCEPT"),"*/*")
    Set ensRequest.pathpetId = petId
    Return ##class(petshop.Utils).invokeHostAsync("petshop.bp.Process", ensRequest, "petshop.bs.ProxyService")
}
...
ClassMethod POSTuploadFile(petId As %String) As %Status
{
    Set ensRequest = ##class(petshop.msg.uploadFileRequest).%New()
    Set ensRequest.consume = %request.ContentType
    Set ensRequest.accept = $Get(%request.CgiEnvs("HTTP_ACCEPT"),"*/*")
    Set ensRequest.pathpetId = petId
    Set ensRequest.formDataadditionalMetadata = $Get(%request.Data("additionalMetadata",1))
    set mime = %request.GetMimeData("file")
    Do:$Isobject(mime) ensRequest.formDatafile.CopyFrom(mime)
    Return ##class(petshop.Utils).invokeHostAsync("petshop.bp.Process", ensRequest, "petshop.bs.ProxyService")
}
...
}

  Así que vamos a probar la producción con este REST proxy.

Abrir e iniciar petshop.Production

Cómo crear una tienda de mascotas

  Modifícalo con tu número de puerto, si es necesario:

curl --location --request POST 'http://localhost:52795/petshoprest/pet' \
--header 'Content-Type: application/json' \
--data-raw '{
  "category": {
    "id": 0,
    "name": "string"
  },
  "id" : 456789,
  "name": "Kitty_Galore",
  "photoUrls": [
    "string"
  ],
  "tags": [
    {
      "id": 0,
      "name": "string"
    }
  ],
  "status": "available"
}'

  La producción se ejecuta en modo asíncrono, así que la rest proxy application no espera la respuesta. Este comportamiento podría editarse, pero generalmente, la producción de interoperabilidad utiliza el modo asíncrono.   Puedes ver el resultado en Message Viewer y en Visual Trace.

Corrección: desde la versión 1.1.0, REST proxy application funciona en el modo de sincronización

  Si todo funciona bien, podremos observar un código de estado http 200. Como puedes ver, recibimos una respuesta del cuerpo y esta no se analizó.

¿Qué quiere decir eso?
Esto ocurre cuando no es respuesta de la aplicación/json o la respuesta 200 de la especificación Swagger no está completa.
En este caso, la respuesta 200 no está completa.  

Consigue una mascota

  Ahora, intenta obtener la mascota que se creó:

curl --location --request GET 'http://localhost:52795/petshoprest/pet/456789'

  Comprueba Visual Trace:

  Esta vez, es una respuesta de la aplicación/json (la respuesta 200 se completó en la especificación de Swagger). Podemos ver el análisis de un objeto de respuesta.   ### API REST - Generar y descargar

Además, esta herramienta puede alojarse en un servidor para permitir que los usuarios generen y descarguen el código.   La API REST y un formulario básico están disponibles:  

En este caso, el código simplemente se genera sin compilar o exportar, y después se elimina por completo.
Esta función puede ser útil para la centralización de herramientas.
  Consulta el archivo README.md para obtener información actualizada.     Gracias por leer el artículo.  

0
0 398
Artículo Dani Fibla · dic 5, 2020 7m read

Cuando se llama a los servicios web, existen varios escenarios para los Business Operation, que actúan juntos para controlar lo que sucederá cuando no se obtenga una respuesta en el tiempo deseado. (Esto también es importante, por ejemplo, en el caso de una solicitud HTTP simple que no pertenezca a los servicios SOAP).

0
0 233
Artículo Daniel Franco · ago 19, 2020 2m read

Ejecutar modelos predictivos de forma nativa en un "Business Process" ("Proceso Empresarial") de InterSystems IRIS siempre ha sido, por supuesto, el objetivo de nuestro soporte para PMML, pero de alguna forma nunca formó parte del paquete porque había algunas dependencias y elecciones que era necesario analizar y decidir. En cualquier caso, gracias a algunas presiones y al código ofrecido amablemente por  @Amir Samary (¡gracias de nuevo, Amir!), finalmente conseguimos empaquetarlo en un repositorio de GitHub para que lo disfruteis, lo valoreis y hagáis sugerencias.

0
0 187
Artículo Bernardo Linarez · jul 1, 2019 16m read

Como todos sabemos, IRIS Database / Caché es un motor de base de datos que efectúa muchas tareas dentro de sí misma. Sin embargo, ¿qué puede hacer cuando necesita tener acceso a una base de datos externa? Una opción es utilizar el SQL Gateway en Caché mediante un conector JDBC. En este artículo, mi objetivo es responder las siguientes preguntas con el fin de ayudarle a que se familiarice con la tecnología y que resuelva algunos de los más problemascomunes.

0
1 886