New post

Find

Discussion (2)3
Log in or sign up to continue
Article
· Feb 11 7m read

Generación de Especificaciones OpenAPI

Introducción

Una API REST (Representational State Transfer) es una interfaz que permite que diferentes aplicaciones se comuniquen entre sí a través del protocolo HTTP, utilizando operaciones estándar como GET, POST, PUT y DELETE. Las API REST son ampliamente utilizadas en el desarrollo de software para exponer servicios accesibles por otras aplicaciones, permitiendo la integración entre diferentes sistemas.

Sin embargo, para garantizar que las API sean fáciles de comprender y utilizar, es fundamental contar con una buena documentación. Aquí es donde entra en juego OpenAPI.

OpenAPI es un estándar para la descripción de API RESTful. Permite definir de manera estructurada el funcionamiento de una API, especificando los endpoints disponibles, los tipos de datos aceptados y devueltos, los parámetros requeridos y las respuestas esperadas. Toda esta información se recopila en un archivo de especificación (generalmente con extensión .yaml o .json), que puede ser interpretado por herramientas automáticas para generar código, documentación y más.

La especificación OpenAPI está diseñada para ser legible tanto por máquinas como por humanos, permitiendo describir, producir, consumir y visualizar servicios web RESTful de manera estandarizada. Por lo tanto, un documento OpenAPI representa una descripción formal de la API, útil tanto para los desarrolladores que necesitan utilizarla como para las herramientas que pueden aprovecharla para automatizar diversos procesos.

¿Por qué es útil definir un archivo de especificación?

Adoptar OpenAPI para documentar una API ofrece varios beneficios:

  • Claridad: Proporciona una documentación detallada y estructurada, permitiendo a los desarrolladores comprender rápidamente cómo interactuar con la API, qué solicitudes enviar y qué datos esperar en la respuesta.
  • Automatización: La documentación puede generarse automáticamente a partir del código, manteniéndose siempre actualizada con los cambios en la API.
  • Interactividad: Existen herramientas como Swagger, un conjunto de soluciones de código abierto para la documentación y prueba de API REST, que incluyen Swagger UI. Esta permite explorar y probar las API directamente desde el navegador, simplificando el desarrollo, la verificación y la comprensión de las API.
  • Estandarización: Al utilizar OpenAPI, la documentación sigue un formato compartido y reconocido, facilitando la integración con otras herramientas y servicios.

 

¿Cómo se puede generar un documento OpenAPI?

Existen dos enfoques principales para generar un archivo de especificación OpenAPI:

  • Enfoque "Code-first" (automático): Si ya se ha desarrollado una API REST en InterSystems IRIS, se puede generar automáticamente la documentación OpenAPI sin necesidad de escribir manualmente el archivo de especificación. IRIS ofrece una funcionalidad integrada que permite exportar la documentación OpenAPI en formato JSON o YAML, basándose en las definiciones de las clases REST.
  • Enfoque "Specification-first" (manual): En este caso, el archivo OpenAPI se escribe manualmente en YAML o JSON, describiendo todos los endpoints, parámetros y respuestas esperadas. Este enfoque es útil cuando se desea definir la API antes de implementarla, facilitando su diseño y la comunicación con otros desarrolladores o partes interesadas.

 

Enfoque Automático

Existen dos formas de generar automáticamente el archivo de especificación OpenAPI en InterSystems IRIS.

Método 1: Usando la función GetWebRESTApplication

Un enfoque es utilizar la función GetWebRESTApplication proporcionada por la clase %REST.API.
Un ejemplo práctico de cómo utilizarla es agregando la siguiente función en la clase de dispatch:

ClassMethod GenerateOpenAPI() As %Status
{
    // The name of the REST application
    Set webApplication = "MyAPP"  // Replace with the name of your web app
    // Retrieve the OpenAPI 2.0 documentation
    Set sc = ##class(%REST.API).GetWebRESTApplication("", webApplication, .swagger)
    
    If $$$ISERR(sc) {
        Quit sc  // If an error occurred, exit the method
    }
    
    // Return the documentation in JSON format
    Set %response.ContentType = "application/json"
    Do ##class(OMRREST.impl).%WriteResponse(swagger.%ToJSON()) 

    Quit $$$OK
}

Además, agrega la siguiente ruta a la UrlMap:

  <Route Url="/openapi" Method="GET" Call="GenerateOpenAPI"/>

En este punto, tendrás todo lo necesario para generar el archivo de especificación desde tu clase de dispatch. Para visualizar la documentación, conéctate a la URL indicada (donde MyWebapp es el nombre de tu aplicación web, tal como está definida en el portal de administración):

<host>:<port>/MyWebapp/openapi

El JSON generado de esta manera representa la especificación OpenAPI de tu API. Después de explorar el segundo método, veremos cómo visualizarlo y probarlo en Swagger.

Método 2: Usando la API de Gestión

Otra forma de generar el archivo de especificación OpenAPI es utilizando la API de Gestión de InterSystems IRIS.

Para llamar a este servicio, puedes usar herramientas como Postman, que es una herramienta para desarrolladores que permite probar, documentar y automatizar APIs.
Postman proporciona una interfaz simple e intuitiva para enviar solicitudes HTTP (GET, POST, PUT, DELETE, etc.), ver respuestas, gestionar autenticaciones y crear pruebas automatizadas.

Para realizar la solicitud utilizando Postman, sigue estos pasos:

  1. Haz clic en el botón New y crea una solicitud HTTP.
  2. Configura la solicitud de la siguiente manera y envíala:
    • Selecciona GET como el método HTTP.
    • Especifica la URL en el siguiente formato, utilizando el <baseURL> de tu instancia:
      https://<baseURL>/api/mgmnt/v1/namespace/myapp
      Aquí, namespace es el nombre del espacio de nombres donde has creado el servicio REST, y myapp es el nombre de tu aplicación web.
    • Configura el método de autorización como Basic Auth y proporciona el nombre de usuario y la contraseña de un usuario con acceso de lectura al espacio de nombres especificado.

Una vez que se genere el JSON, se podrá visualizar y probar utilizando herramientas como Swagger Editor.

{
   "info":{
      "title":"",
      "description":"",
      "version":"",
      "x-ISC_Namespace":"MyNamespace"
   },
   "basePath":"/MyWebapp",
   "paths":{
      "/loginForm":{
         "post":{
            "parameters":[
               {
                  "name":"payloadBody",
                  "in":"body",
                  "description":"Request body contents",
                  "required":false,
                  "schema":{
                     "type":"string"
                  }
               }
            ],
            "operationId":"loginForm",
            "x-ISC_ServiceMethod":"loginForm",
            "responses":{
               "default":{
                  "description":"(Unexpected Error)"
               },
               "200":{
                  "description":"(Expected Result)"
               }
            }
         }
      },
      "/refresh":{
         "post":{
            "parameters":[
               {
                  "name":"payloadBody",
                  "in":"body",
                  "description":"Request body contents",
                  "required":false,
                  "schema":{
                     "type":"string"
                  }
               }
            ],
            "operationId":"refresh",
            "x-ISC_ServiceMethod":"refresh",
            "responses":{
               "default":{
                  "description":"(Unexpected Error)"
               },
               "200":{
                  "description":"(Expected Result)"
               }
            }
         }
      },
      "/logout":{
         "post":{
            "parameters":[
               {
                  "name":"payloadBody",
                  "in":"body",
                  "description":"Request body contents",
                  "required":false,
                  "schema":{
                     "type":"string"
                  }
               }
            ],
            "operationId":"logout",
            "x-ISC_ServiceMethod":"logout",
            "responses":{
               "default":{
                  "description":"(Unexpected Error)"
               },
               "200":{
                  "description":"(Expected Result)"
               }
            }
         }
      },
      "/openapi":{
         "get":{
            "operationId":"GenerateOpenAPI",
            "x-ISC_ServiceMethod":"GenerateOpenAPI",
            "responses":{
               "default":{
                  "description":"(Unexpected Error)"
               },
               "200":{
                  "description":"(Expected Result)"
               }
            }
         }
      }
   },
   "swagger":"2.0"
}

Enfoque Manual

El enfoque manual para generar un documento OpenAPI consiste en escribir el archivo de especificación manualmente en formato YAML o JSON. Este enfoque es especialmente útil cuando se desea tener un control total sobre el diseño de la API antes de su implementación, o cuando se documenta una API ya existente sin depender de herramientas automatizadas.

Para escribir el archivo de especificación OpenAPI, puedes consultar la documentación oficial de la versión 2.0 de la OpenAPI Specification, donde encontrarás información sobre los campos requeridos y cómo describir los endpoints, parámetros, respuestas y más. Esta guía detallada te ayudará a comprender cómo estructurar correctamente el archivo YAML o JSON para cumplir con los estándares de OpenAPI.

Un buen ejemplo de este enfoque es cuando se crea un servicio REST utilizando los métodos descritos en la documentación oficial de InterSystems IRIS. Puedes encontrar una introducción sobre cómo desarrollar y configurar una aplicación REST en IRIS en la siguiente página de la documentación, que describe paso a paso los métodos necesarios para exponer una aplicación RESTful con IRIS.

2 Comments
Discussion (2)2
Log in or sign up to continue
Article
· Feb 11 4m read

%Status e Exceptions do IRIS

Você pode encontrar erros durante qualquer ponto da execução do programa, e existem várias maneiras de levantar e tratar essas exceções. Neste artigo, exploraremos como as exceções são tratadas de forma eficiente no IRIS.

Um dos tipos de retorno mais comumente usados é %Status, que é usado por métodos para indicar sucesso ou falha. Vamos começar discutindo os valores de %Status.

Trabalhando com %Status

O tipo de retorno %Status é usado para representar erros ou sucesso. Muitos métodos do sistema retornam %Status quando ocorre um erro. Você pode criar uma estrutura semelhante para os erros do seu aplicativo ou converter para %Status, mesmo quando você levantou exceções em seu código.

Vamos começar a criar os erros.

Macros

Quando falamos sobre erros, as Macros são essenciais e facilitam a criação de valores de Status no código. O IRIS fornece várias macros para criar e tratar erros dentro do código do seu aplicativo.

A macro mais comumente usada é $$$ERROR.

$$$ERROR

A macro $$$ERROR é especificamente projetada para gerar e retornar um valor %Library.%Status. É sua responsabilidade verificar o status antes de continuar a execução do seu programa. Esta macro está intimamente ligada a erros gerais do sistema. Tenha os seguintes pontos em mente ao usar esta macro:

-O primeiro argumento (códigos de erro) se refere aos códigos de erro gerais dentro do arquivo include %occErrors.
-Se você estiver usando erros predefinidos desse arquivo include, você pode usar a macro diretamente, com ou sem argumentos adicionais.

Observação: Antes de prosseguir, $SYSTEM.OBJ.DisplayError(status) ou $SYSTEM.Status.DisplayError(status)eram usados para exibir o(s) erro(s) e oferecem suporte à localização de strings. 

Set sc = $$$ERROR($$$NamespaceDoesNotExist,"TEST")
Do $SYSTEM.OBJ.DisplayError(sc)

output: ERROR #5015: Namespace 'TEST' does not exist1
Set sc = $$$ERROR($$$UserCTRLC)
Do $SYSTEM.OBJ.DisplayError(sc)

ouput: ERROR #834: Login aborted1

Se você usar qualquer outro código de erro que não esteja listado em %occErrors, um erro "Código de status desconhecido" será retornado. Sempre use o código de erro predefinido $$$GeneralError == 5001 para mensagens de erro gerais.

Exemplo de código de status desconhecido

Set sc = $$$ERROR(95875,"TEST Error")
Do $SYSTEM.Status.DisplayError(sc)

output: ERROR #95875: Unknown status code: 95875 (TEST Error)
Set sc = $$$ERROR($$$GeneralError,"TEST Error")
Do $SYSTEM.OBJ.DisplayError(sc)

output: ERROR #5001: TEST Error

$$$ADDSC

O %Status não contém necessariamente apenas um erro. Seu programa pode validar várias condições e manter o controle de todos os erros em um único status, e então usar a macro $$$ADDSC. Por exemplo, o método %ValidateObject() pode retornar múltiplos erros em uma única resposta, que usa essa funcionalidade.

A macro $$$ADDSC anexa um status a outro, e o método $SYSTEM.Status.AppendStatus executa a mesma função.

$$$ADDSC(sc1,sc2) / $SYSTEM.Status.AppendStatus(s1,s2) - anexa sc2 a sc1 e retorna um novo código de status.

ClassMethod AppendErrors()
{
    Set sc1 = $$$ERROR($$$UserCTRLC) 
    Set sc2 = $$$ERROR($$$NamespaceDoesNotExist,"TEST")
    Set sc = $$$ADDSC(sc1,sc2)
    Do $SYSTEM.Status.DisplayError(sc)
    Write !
    Set sc = $$$ADDSC(sc1,sc2)
    Do $SYSTEM.Status.DisplayError(sc)
}
output
LEARNING>do ##class(Learning.ErrorHandling).AppendErrors()
 
ERROR #834: Login aborted
ERROR #5015: Namespace 'TEST' does not exist
 
ERROR #834: Login aborted
ERROR #5015: Namespace 'TEST' does not exist

ambos os resultados são iguaias!

$$$GETERRORCODE

$$$GETERRORCODE(status) - Retorna o valor do código de erro do Status. Esta macro pertence ao arquivo %occStatus.inc

    Set status = $$$ERROR($$$UserCTRLC) 
    Write $$$GETERRORCODE(status),!
    #;output: 834
    Set status = $$$ERROR($$$GeneralError,"TEST Error")
    Write $$$GETERRORCODE(status)
    #;output: 5001

$$$GETERRORMESSAGE
$$$GETERRORMESSAGE(sc,num) - Esta macro retorna a parte do erro baseada no número (num) no %Status.
Aqui está um exemplo.

ClassMethod GetErrorMsgMacro()
{
	Set status = $$$ERROR($$$GeneralError,"TEST Error",1,$$$ERROR($$$GeneralError,"TEST Error2"))
	write $$$GETERRORMESSAGE(status),! ; It prints "TEST Error"
	#;
	Set st = $$$GETERRORMESSAGE(status,3) ; it get the "$$$ERROR($$$GeneralError,"TEST Error2")" in 3rd position based on the 2nd argument 
	write $$$GETERRORMESSAGE(st),! ; it prints TEST Error2
}

LEARNING>Do ##class(Learning.myexcept).GetErrorMsgMacro()
TEST Error
TEST Error2

 

Validar Status de Retorno

Agora que você criou o erro e o retornou para o seu programa, o próximo passo é validar se a resposta de retorno é bem-sucedida ou errônea.

A macro $$$ISERR verifica se o status representa um erro. Ela retorna 1 se o status indicar um erro, caso contrário, retorna 0. Existe outra macro, $$$ISOK, que retorna 1 quando o status é bem-sucedido.

Exibindo Erros

Se o seu programa apresentar erros inesperadamente (sempre espere o inesperado), você precisa exibir a mensagem de erro. A classe %SYSTEM.Status é especificamente projetada para visualizar ou criar valores %Status (se você preferir não usar macros) e já vimos os exemplos acima.

Aqui estão os métodos equivalentes em %SYSTEM.Status que substituem as macros:

Macro Methods
$$$ISERR $SYSTEM.Status.IsError()
$$$ISOK $SYSTEM.Status.IsOK()
$$$ADDSC $SYSTEM.Status.AppendStatus()
$$$ERROR $SYSTEM.Status.Error()
$$$OK $SYSTEM.Status.OK()
$$$GETERRORCODE $SYSTEM.Status.GetErrorCodes()

As exceções continuarão no próximo artigo.

Discussion (0)1
Log in or sign up to continue
Article
· Feb 11 5m read

Getting Started Using Istio Service Mesh with Mirrored IRIS Environment in Kubernetes

The Istio Service Mesh is commonly used to monitor communication between services in applications. The "battle-tested" sidecar mode is its most common implementation. It will add a sidecar container to each pod you have in your namespace that has Istio sidecar injection enabled.

It's quite easy to get started with, just put the istioctl executable in your PATH, and label your namespace such that it tells Istio to acitvate side car injection there.

>> kubectl get po -n iris
NAME                                              READY   STATUS    RESTARTS        AGE
intersystems-iris-operator-amd-8588f64559-xqsfz   1/1     Running   0               52s
>> istioctl install
This will install the Istio 1.22.2 "default" profile (with components: Istio core, Istiod, and Ingress gateways) into the cluster. Proceed? (y/N) y
✔ Istio core installed
✔ Istiod installed
✔ Ingress gateways installed
✔ Installation complete                                                                                                                                     Made this installation the default for injection and validation.
>> kubectl label namespace iris istio-injection=enabled
namespace/iris labeled
>> kubectl delete po intersystems-iris-operator-amd-8588f64559-xqsfz -n iris
pod "intersystems-iris-operator-amd-8588f64559-xqsfz" deleted
>> kubectl get po -n iris
NAME                                              READY   STATUS    RESTARTS     AGE
intersystems-iris-operator-amd-8588f64559-c4gfh   2/2     Running   1 (8s ago)   12s

For example, if our pods previously had ready: 1/1 you will now see 2/2, the second pod being the sidecar (note that this will require a pod restart if the pod was up before the namespace was labeled).
See Open Exchange App for GitHub Repo and find irisSimple.yaml to understand this deployment.

>> kubectl get po -n iris
NAME                                              READY   STATUS    RESTARTS        AGE
intersystems-iris-operator-amd-8588f64559-c4gfh   2/2     Running   1 (9m10s ago)   9m14s
iris-data-0                                       2/2     Running   0               64s
iris-webgateway-0                                 2/2     Running   0               28s

Similarly, if I were to have had a WebGateway sidecar, we would see the data node have 3 pods in the container (IRIS, WG, Istio):

See Open Exchange App for GitHub Repo and find irisSimpleSideCar.yaml to understand this deployment

>> kubectl get po -n iris
NAME                                              READY   STATUS    RESTARTS      AGE
intersystems-iris-operator-amd-8588f64559-c4gfh   2/2     Running   1 (11m ago)   11m
iris-data-0                                       3/3     Running   0             35s
iris-webgateway-0                                 2/2     Running   0             2m43s

The idea is that Istio will receive and forward all communication without needing user intervention. The problem with such black boxes, is that when something goes wrong it can be difficult to understand why.

Let's set up a mirrored architecture and see what happens.

First thing you will notice is that iris-data-0-1 will take an extremely long time to start-up. In my tests is is about ~65 minutes. This is because it is attempting to connect to the arbiter and other data node but is not succeeding. There is a timer that is out of the scope of this article that says "after one hour go up with or without mirror setup".

See Open Exchange App for GitHub Repo and find irisMirrorSideCar.yaml to understand this deployment.

kubectl get po -w -n iris
NAME                                              READY   STATUS    RESTARTS       AGE
intersystems-iris-operator-amd-8588f64559-c4gfh   2/2     Running   1 (117m ago)   117m
iris-arbiter-0                                    2/2     Running   0              61m
iris-data-0-0                                     3/3     Running   0              61m
iris-data-0-1                                     2/3     Running   0              60m

Eventually (a bit more than 60 minutes after) we get the following:

>> kubectl get po -n iris
NAME                                              READY   STATUS    RESTARTS        AGE
intersystems-iris-operator-amd-8588f64559-c4gfh   2/2     Running   1 (3h26m ago)   3h26m
iris-arbiter-0                                    2/2     Running   0               150m
iris-data-0-0                                     3/3     Running   0               149m
iris-data-0-1                                     3/3     Running   0               149m
iris-webgateway-0                                 2/2     Running   0               87m

Looking into the logs we see:

2 [Utility.Event] Error creating MirrorSet 'irismirror1' member 'backup': ERROR #2087: Mirror member IRIS-DATA-0-0 is unreachable with IRIS-DATA-0-0.IRIS-SVC.DEFAULT.SVC.CLUSTER.LOCAL,1972

Istio's automatic service and endpoint detection has failed to pass on communications between the arbiter, backup, and primary.

It identifies, by default, that mTLS is being used even though it is not (at least in this situation). In order to get around this we can explicitly tell Istio not to use mTLS. We do this as follows:

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: disable-mtls
  namespace: default
spec:
  mtls:
    mode: DISABLE

Once we unblock the communications we get a properly configured mirror:

And finally, we can properly harness the power of Istio, for example with View The Dashboard

Istio has many Custom Resource Definitions, and Peer Authentication is not the only one you may want to look deeper into. Virtual Services and Destination Rule also warrant a look.

 

If you do want to set up mTLS I would point you to these three sources:

1) InterSystems Official mTLS documentation

2) Steve Pisani's great step-by-step explainer

3) IKO Docs - TLS Security (note that some connections here are mTLS while some are only TLS)

1 Comment
Discussion (1)1
Log in or sign up to continue
Announcement
· Feb 11

VS Code release January 2025 (version 1.97)

Visual Studio Code releases new updates every month with new features and bug fixes, and the January 2025 release is now available. 

This release enhances AI-assisted editing, customization and security as well as improvements for debugging and log management for a more seamless development experience. If you need help migrating from InterSystems Studio to VS Code, or want to advance your knowledge of VS Code, take a look at the training courses George James Software offers > georgejames.com/vscode-training/

Version 1.97 now includes: 

The release also includes contributions from our very own @John Murray through pull requests that address open issues. 

Find out more about these features in the release notes here > https://code.visualstudio.com/updates/v1_97

For those with VS Code, your environment should auto-update. You can manually check for updates by running Help > Check for Updates on Linux and Windows or running Code > Check for Updates on macOS.

Discussion (0)2
Log in or sign up to continue