En el artículo anterior, Generación de Especificaciones OpenAPI, vimos qué es OpenAPI, por qué es útil para documentar los servicios REST y cómo generar un archivo de especificación en formato .yaml
o .json
. También exploramos cómo IRIS permite crear automáticamente esta documentación a partir del código existente, siguiendo el enfoque code-first.
Pero, ¿qué sucede si partimos del archivo OpenAPI en lugar del código? En otras palabras, ¿si primero definiéramos el comportamiento de nuestra API y luego generáramos las clases necesarias para implementarla en IRIS? Este método, conocido como enfoque specification-first, permite diseñar un servicio de manera clara y coherente, incluso antes de escribir la lógica de aplicación.
En este artículo veremos cómo utilizar este enfoque para construir una REST API completa en Intersystems IRIS a partir de una especificación OpenAPI 2.0.
Primer paso: construcción del archivo OpenAPI
El archivo OpenAPI 2.0 es un documento en formato JSON (o YAML) en el que se describen todas las API de la aplicación: las rutas disponibles (endpoints), los métodos HTTP soportados (GET, POST, PUT, DELETE...), los parámetros aceptados, las respuestas esperadas y mucho más.
Para construir correctamente este archivo, es fundamental conocer la estructura definida por el estándar OpenAPI 2.0
Algunos recursos útiles para profundizar son:
A continuación, se muestra un ejemplo sencillo de archivo openapi.json que define una API con un endpoint GET /hello
:
{
"swagger": "2.0",
"info": {
"title": "API example",
"description": "Example of openAPI file",
"version": "1.0.0"
},
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"paths": {
"/hello": {
"get": {
"summary": "Sample greeting",
"description": "Returns a greeting message",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
},
"operationId": "hello"
}
}
}
}
Segundo paso: generación de las clases de la interfaz REST
Una vez creado el archivo de especificación, es posible utilizar una de las herramientas de gestión de APIs de InterSystems IRIS para generar automáticamente las tres clases principales que constituyen un servicio REST:
- Clase de especificación (
.spec
)
Subclase de %REST.Spec
, que contiene la especificación OpenAPI 2.0 del servicio REST.
- Clase de dispatch (
.disp
)
Subclase de %CSP.REST
, responsable de recibir las solicitudes HTTP y de invocar los métodos apropiados en la clase de implementación.
- Clase de implementación (
.impl
)
Subclase de %REST.Impl
, contiene la implementación de la lógica de los métodos REST.
En este artículo se muestra cómo generar el servicio REST utilizando el servicio /api/mgmnt/
.
Generación de las clases utilizando el servicio /api/mgmnt
- Utilizar Postman (o una herramienta similar) para crear un mensaje de solicitud HTTP como sigue:
- Para la acción HTTP, seleccionar POST.
- Para la URL, especificar una URL en el siguiente formato, usando la <baseURL> de tu instancia: https://<baseURL>/api/mgmnt/v2/namespace/myapp Donde namespace es el espacio de nombres en el que se desea crear el servicio REST y myapp es el nombre del paquete en el que se desea crear las clases. Ejemplo:
http://localhost:1888/api/mgmnt/v2/myNamespace/Greetings
- En el cuerpo de la solicitud, pegar la especificación OpenAPI 2.0 de tu servicio web en formato JSON.
- Especificar el tipo de contenido del cuerpo como JSON (application/json).
- Como método de autenticación, usar Basic Auth e introducir como username y password las credenciales de un usuario con permisos de lectura/escritura sobre el espacio de nombres indicado.
.png)
.png)
- Enviar el mensaje de solicitud.
- Caso de creación del servicio REST: si la llamada tiene éxito, InterSystems IRIS crea las clases
.disp
, .impl
y .spec
en el paquete del namespace especificado, y en Postman se recibirá el siguiente mensaje de confirmación: { "msg": "New application myapp created" }
- Caso de actualización del servicio REST: si la llamada tiene éxito, se recibirá el mensaje:
{"msg": "Application lombardia.GUIConvenzioni.OMRREST updated"}
Además, InterSystems IRIS regenerará completamente las clases .disp
y .spec
en el paquete especificado, y actualizará la clase .impl
, conservando el código previamente implementado en ella. Atención: cualquier modificación manual realizada a la clase .disp
será sobrescrita cada vez que se actualice la especificación, por lo que no se recomienda modificarla directamente.
Tercer paso: implementación de la lógica REST
Una vez generadas las tres clases (.spec
, .disp
, .impl
), es momento de completar el servicio REST implementando la lógica de las operaciones.
Veamos un ejemplo concreto basado en la especificación mostrada anteriormente (que define un endpoint GET /hello
).
Las clases generadas serán:
Clase Greetings.spec
Esta clase contiene la definición OpenAPI en formato JSON. No requiere modificaciones manuales, pero puede ser actualizada regenerándola mediante el servicio /api/mgmnt/
cada vez que se modifique la especificación.
Class Greetings.spec Extends Extends %REST.Spec [ ProcedureBlock ]
{
XData OpenAPI [ MimeType = application/json ]
"swagger": "2.0",
"info": {
"title": "API example",
"description": "Example of openAPI file",
"version": "1.0.0"
},
...
}
}
Clase Greetings.disp
Es la clase de dispatch la que conecta las rutas HTTP con los métodos de implementación. No es necesario (ni recomendable) modificarla, ya que se regenera cada vez que se actualiza la especificación.
Class Greetings.disp Extends %CSP.REST [ GeneratedBy = Greetings.spec.cls, ProcedureBlock ]
{
Parameter SpecificationClass = "Greetings.spec"
Parameter IgnoreWrites = 1
Parameter CONTENTTYPE = "application/json"
Parameter CONVERTINPUTSTREAM = 1
Parameter CHARSET = "utf-8"
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes>
<Route Url="/hello" Method="GET" Call="hello"/>
</Routes>
ClassMethod hello() As %Status
{
Try {
Do ##class(%REST.Impl).%SetContentType("application/json")
If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
Set response=##class(Greetings.impl).hello()
Do ##class(Greetings.impl).%WriteResponse(response)
} Catch (ex) {
Do ##class(%REST.Impl).%ReportRESTError(..#HTTP500INTERNALSERVERERROR,ex.AsStatus(),$parameter("Greetings.impl","ExposeServerExceptions"))
}
Quit $$$OK
}
}
Como se puede observar, esta clase mapea el endpoint /hello
al método hello()
presente en la clase Greetings.impl
.
Clase Greetings.impl
Esta es la clase en la que se escribe la lógica de aplicación propiamente dicha. Es una subclase de %REST.Impl
y se genera con métodos “vacíos” que deben ser implementados.
Class Greetings.impl Extends %REST.Impl [ ProcedureBlock ]
{
ClassMethod hello() As %Status
{
}
}
Conclusión
Hemos visto cómo, a partir de una especificación OpenAPI 2.0, es posible construir una interfaz REST completa en InterSystems IRIS siguiendo el enfoque specification-first. Con unos pocos pasos, el framework nos proporciona una estructura lista para usar sobre la cual integrar nuestra lógica de negocio, permitiéndonos desarrollar APIs de manera clara, ordenada y escalable.
En el próximo artículo veremos cómo exponer efectivamente el servicio REST a través de una aplicación web de IRIS.