#REST API

0 Abonnés · 43 Publications

Le transfert d'état représentationnel (Representational state transfer, REST) est un style architectural logiciel qui définit un ensemble de contraintes à utiliser pour créer des services web. Les services web conformes au style architectural REST, appelés Web RESTful Services (RWS), assurent l'interopérabilité entre les systèmes informatiques sur Internet. Les Web RESTful Services permettent aux systèmes demandeurs d'accéder aux représentations textuelles des ressources web et de les manipuler en utilisant un ensemble uniforme et prédéfini d'opérations sans état. D'autres types de services web, tels que les Web SOAP Services, fournissent leurs propres ensembles arbitraires d'opérations.

En savoir plus.

Article Iryna Mykhailova · Nov 5, 2025 6m read

Salut!

C'est encore moi 😁. Dans l'article précédent Comment écrire un service API REST pour exporter le paquet FHIR généré au format JSON, nous avons généré une ressource DocumentReference, dont le contenu était encodé en Base64

Question!! Est-il possible d'écrire un service REST pour le décoder? Je voudrais vraiment savoir ce que contiennent les données du message🤔🤔🤔

Bien, allons-y!

1. Créez une nouvelle classe utilitaire datagen.utli.decodefhirjson.cls pour décoder les données contenues dans DocumentReference
 

Class datagen.utli.decodefhirjson Extends%RegisteredObject
{
}

2. Écrivez une fonction Python decodebase64docref pour 
a. parcourir le paquet FHIR
b. découvrir la ressource DocumentReference
  - récupérer le premier élément dans le contenu
   - récupérer la pièce jointe à partir de  contenu
     - récupérer les données à partir de la pièce jointe
c. décoder les données par Base64
 (pour cette partie, j'ai demandé l'aide de Chat-GPT😂🤫) 

Class datagen.utli.decodefhirjson Extends%RegisteredObject
{

ClassMethod decodebase64docref(fhirbundle = "") As%String [ Language = python ] { # w##class(datagen.utli.decodefhirjson).decodebase64docref() import base64 import json

def decode_discharge_summary(bundle_json):
    <span class="hljs-string">"""
    Extracts and decodes the Base64-encoded discharge summary note
    from a FHIR Bundle containing a DocumentReference.
    """</span>
    <span class="hljs-keyword">for</span> entry in bundle_json.get(<span class="hljs-string">"entry"</span>, []):
        resource = entry.get(<span class="hljs-string">"resource"</span>, {})
        <span class="hljs-keyword">if</span> resource.get(<span class="hljs-string">"resourceType"</span>) == <span class="hljs-string">"DocumentReference"</span>:
            # Traverse to the attachment
            content_list = resource.get(<span class="hljs-string">"content"</span>, [])
            <span class="hljs-keyword">if</span> not content_list:
                <span class="hljs-keyword">continue</span>
            attachment = content_list[<span class="hljs-number">0</span>].get(<span class="hljs-string">"attachment"</span>, {})
            base64_data = attachment.get(<span class="hljs-string">"data"</span>)
            <span class="hljs-keyword">if</span> base64_data:
                decoded_text = base64.b64decode(base64_data).decode(<span class="hljs-string">"utf-8"</span>)
                <span class="hljs-keyword">return</span> decoded_text
    <span class="hljs-keyword">return</span> None


# Example usage
# Load your FHIR Bundle JSON from a file or object
#with <span class="hljs-keyword">open</span>(<span class="hljs-string">"fhir_bundle.json"</span>, <span class="hljs-string">"r"</span>) <span class="hljs-keyword">as</span> f:
#    fhir_bundle = json.loads(f)
<span class="hljs-keyword">if</span> fhirbundle==<span class="hljs-string">""</span>:
    rtstr=f<span class="hljs-string">"&#x26a0;&#xfe0f; No input found - JSON string is required."</span>
    jsstr={<span class="hljs-string">"operation_outcome"</span> : rtstr}
    <span class="hljs-keyword">return</span> json.dumps(jsstr, indent=<span class="hljs-number">2</span>)

fhir_bundle = json.loads(fhirbundle)
decoded_note = decode_discharge_summary(fhir_bundle)

<span class="hljs-keyword">if</span> decoded_note:
    #<span class="hljs-keyword">print</span>(<span class="hljs-string">"&#x1f4dd; Decoded Discharge Summary:\n"</span>)
    #<span class="hljs-keyword">print</span>(decoded_note)
    rtstr=f<span class="hljs-string">"&#x1f4dd; Decoded Discharge Summary:\n {decoded_note}"</span>
<span class="hljs-keyword">else</span>:
    #<span class="hljs-keyword">print</span>(<span class="hljs-string">"&#x26a0;&#xfe0f; No DocumentReference with Base64 note found."</span>)
    rtstr=f<span class="hljs-string">"&#x26a0;&#xfe0f; No DocumentReference with Base64 note found."</span>
jsstr={<span class="hljs-string">"data"</span> : rtstr}
<span class="hljs-keyword">return</span> json.dumps(jsstr, indent=<span class="hljs-number">2</span>)

}

}

Pour tester cette fonction, j' essaie une astuce qui consiste à utiliser la fonction genfhirbundle pour générer un paquet FHIR dans une chaîne JSON,  comme expliqué dans l'article précédent Comment écrire un service API REST pour exporter le paquet FHIR généré au format JSON 

Générons un paquet FHIR et stockons-le dans une variable jsonstr

set jsonstr=##class(datagen.utli.genfhirjson).genfhirbundle(1)

Testons la fonction de décodage decodebase64docref  avec jsonstr

w##class(datagen.utli.decodefhirjson).decodebase64docref(jsonstr)

Bien 😉 Ça semble correct. Je peux désormais lire le message décodé.


Maintenant,  revenez à l'article précédent Comment écrire un service API REST pour exporter les données patient générées au format .csv

Nous aimerions ajouter une nouvelle fonction et mettre à jour le chemin d'accès pour la classe datagen.restservice .

1. Ajoutez une nouvelle fonction DecodeDocRef, qui est censée traiter le paquet FHIR  joint dans le corps au format JSON.

Par exemple, dans ce cas, nous prévoyons un POST.

Le contenu du corps est packagé par défaut sous la forme %CSP.BinaryStream et stocké dans la variable %request.Content, nous pouvons donc utiliser la fonction .Read()  à partir de la classe %CSP.BinaryStream pour lire le BinaryStream

ClassMethod DecodeDocRef() As%Status
{
    // récupération du corps - chaîne json#dim bistream As%CSP.BinaryStream =""set bistream=%request.Contentset jsstr=bistream.Read()
<span class="hljs-comment">//décodage des données de référence du document</span>
<span class="hljs-keyword">w</span> <span class="hljs-keyword">##class</span>(datagen.utli.decodefhirjson).decodebase64docref(jsstr)
<span class="hljs-keyword">return</span> <span class="hljs-built_in">$$$OK</span>

}

2. Ensuite, nous ajoutons un chemin d'accès pour le service REST et compilons😀

<Route Url="/decode/docref" Method="POST" Call="DecodeDocRef" />

La classe  datagen.restservice mise à jour se présentera comme suit.


Génial!! C'est tout ce qu'il y a à faire!😁

Nous allons vérifier cela dans Postman!!

COLLEZ le chemin d'accès  suivant

localhost/irishealth/csp/mpapp/decode/docref

avec le corps suivant (j'ai utilisé un paquet FHIR simplifié, vous pouvez tester le paquet complet😀)

{
    "resourceType": "Bundle",
    "type": "transaction",
    "id": "98bfce83-7eb1-4afe-bf2b-42916512244e",
    "meta": {
        "lastUpdated": "2025-10-13T05:49:07Z"
    },
    "entry": [
        {
            "fullUrl": "urn:uuid:5be1037d-a481-45ca-aea9-2034e27ebdcd",
            "resource": {
                "resourceType": "DocumentReference",
                "id": "5be1037d-a481-45ca-aea9-2034e27ebdcd",
                "status": "current",
                "type": {
                    "coding": [
                        {
                            "system": "http://loinc.org",
                            "code": "18842-5",
                            "display": "Discharge summary"
                        }
                    ]
                },
                "subject": {
                    "reference": "9e3a2636-4e87-4dee-b202-709d6f94ed18"
                },
                "author": [
                    {
                        "reference": "2aa54642-6743-4153-a171-7b8a8004ce5b"
                    }
                ],
                "context": {
                    "encounter": [
                        {
                            "reference": "98cd848b-251f-4d0b-bf36-e35c9fe68956"
                        }
                    ]
                },
                "content": [
                    {
                        "attachment": {
                            "contentType": "text/plain",
                            "language": "en",
                            "data": "RGlzY2hhcmdlIHN1bW1hcnkgZm9yIHBhdGllbnQgOWUzYTI2MzYtNGU4Ny00ZGVlLWIyMDItNzA5ZDZmOTRlZDE4LiBEaWFnbm9zaXM6IFN0YWJsZS4gRm9sbG93LXVwIGluIDIgd2Vla3Mu",
                            "title": "Discharge Summary Note"
                        }
                    }
                ]
            },
            "request": {
                "method": "POST",
                "url": "DocumentReference"
            }
        }
    ]
}

Ça marche parfaitement!!!😆😉

Merci infiniment à tous d'avoir pris le temps de lire cet article. 😘

0
0 10
Article Iryna Mykhailova · Nov 3, 2025 13m read

Bonjour à tous,

Continuons à travailler sur la génération de données de test et l'exportation des résultats via une API REST. 😁

Ici, je souhaite réutiliser la classe `datagen.restservice` créée dans l'article précédent : « Écriture d'un service API REST pour exporter les données patient générées au format .csv ».

Cette fois-ci, nous prévoyons de générer un bundle FHIR incluant plusieurs ressources pour tester le référentiel FHIR.

Voici une référence si vous souhaitez en savoir plus sur FHIR : « The Concept of FHIR: A Healthcare Data Standard Designed for the Future ».

C'est parti ! 😆

0
0 10
Article Iryna Mykhailova · Oct 23, 2025 6m read

Salut,

C'est moi encore 😁. Je travaille actuellement à la génération de fausses données patients à des fins de test avec Chat-GPT et Python. J'aimerais également partager mon apprentissage. 😑

Tout d'abord, créer un service d'API REST personnalisé est facile en utilisant %CSP.REST.

Commençons ! 😂

1. Créez une classe datagen.restservice qui étend %CSP.REST.

Class datagen.restservice Extends%CSP.REST
{
Parameter CONTENTTYPE = "application/json";
}

2. Ajoutez une fonction genpatientcsv() pour générer les données du patient et les regrouper dans une chaîne csv

0
0 18
Article Lorenzo Scalese · Avr 16, 2025 7m read

Qu'est-ce que JWT ??

JWT (JSON Web Token) est un standard ouvert (RFC 7519) qui offre une méthode légère, compacte et autonome pour transmettre en toute sécurité des renseignements entre deux parties. Il est couramment utilisé dans les applications web pour l'authentification, l'autorisation et l'échange d'informations.

Un JWT est généralement composé de trois parties:

1. En-tête JOSE (JSON Object Signing and Encryption)
2. Payload
3. Signature

Ces parties sont encodées au format Base64Url et concaténées avec des points (.) qui les séparent.

Structure d'un JWT

En-tête

{ "alg": "HS256", "typ": "JWT"}

Payload

{"sub": "1234567890", "name": "John Doe", "iat": 1516239022}

Signature:
La signature permet de vérifier que l'expéditeur du JWT est bien celui qu'il prétend être et de s'assurer que le message n'a pas été falsifié.

Pour créer la signature:

1. base64 En-tête et payload encodés en base64.
2. Application de l'algorithme de signature (par exemple, HMAC SHA256 ou RSA) avec une clé secrète (pour les algorithmes symétriques tels que HMAC) ou une clé privée (pour les algorithmes asymétriques tels que RSA).
3. Codage Base64Url du résultat pour obtenir la signature.

Exemple de JWT. Consultez le contenu du JWT 

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Création de JWT dans IRIS

Remarque : Avant 2024, la classe %OAuth2.JWT était utilisée pour générer des JWT dans IRIS. La classe %Net.JSON.JWT est désormais la classe principale pour la création de JWT, et j'utiliserai cette classe dans l'exemple de code.

JWK overview

Les JWK représentent une clé cryptographique, en particulier pour la signature et la vérification des JWT. Les JWK permettent de représenter les clés publiques (pour la vérification) et les clés privées (pour la signature) dans un format normalisé qui peut être facilement échangé entre les systèmes. Les JWKS contiennent plusieurs JWKs

Flux de travail JWT

1. Construisez votre en-tête en tant que %DynamicObject et ajoutez des en-têtes personnalisés si nécessaire.

2. Construisez le corps/les revendications directement en tant que %DynamicObject

3. Appelez la méthode Create de la classe %Net.JSON.JWT.

Set sc = ##Class(%Net.JSON.JWT).Create(header, , claims, jwks, , .JWT)

Création de JWK

Set sc = ##Class(%Net.JSON.JWK).Create("HS256","1212ASD!@#!#@$@#@$$#SDFDGD#%+_)(*@$SFFS",.privateJWK,.publicJWK)

Cela renverra la clé privée

{"kty":"oct","k":"MTIxMkFTRCFAIyEjQCRAI0AkJCNTREZER0QjJStfKSgqQCRTRkZT","alg":"HS256"

Quelques propriétés importantes de JWK

"kty": "oct" - représente l'algorithme symétrique
"kty": "RSA" / "kty": "EC" - represente l'algorithme asymétrique

Une fois que le JWK est créé, il peut être ajouté aux JWKS.

Créons des JWKS dans IRIS

Set sc = ##class(%Net.JSON.JWKS).PutJWK(jwk,.JWKS)

Cette méthode renvoie le JWKS

Génération du JWT dans IRIS

Vous pouvez créer des JWT à clé symétrique ou asymétrique dans IRIS. La classe %Net.JSON.JWK est essentiellement utilisée pour générer le JWT. Avant d'appeler la méthode, assurez-vous de créer et d'envoyer les JWKS pour le chiffrement symétrique et asymétrique lors de la génération du JWT.

Encryptage symétrique

Les algorithmes symétriques utilisent une clé secrète partagée, où l'expéditeur et le destinataire utilisent la même clé pour signer et vérifier le JWT. Ces algorithmes, tels que HMAC (HS256, HS512, HS384), génèrent un hachage (signature) pour le payload du JWT. Cette approche n'est pas recommandée pour les systèmes de haute sécurité, car la signature et la vérification sont exposées, ce qui pose des risques potentiels pour la sécurité.

La méthode Create de la classe %Net.JSON.JWK est utilisée pour générer le JWK. Elle accepte deux paramètres d'entrée et renvoie deux paramètres de sortie:

1. algorithm - l'algorithme pour lequel le JWK doit être créé.
2. secert - la clé utilisée pour signer et vérifier le JWT
3. privateJWK - la clé Web JSON privée qui est créée.
4. publicJWK - la clé Web JSON publique qui est créée.

Pour les algorithmes à clé symétrique, vous obtiendrez privateJWK

Pour les algorithmes à clé asymétrique, vous obtiendrez privateJWK et publicJWK

 
SymmetricKeyJWT

Le résultat 

LEARNING>d ##class(Learning.JWT.NetJWT).SymmetricKeyJWT()
privateJWK={"kty":"oct","k":"MTIxMkFTRCFAIyEjQCRAI0AkJCNTREZER0QjJStfKSgqQCRTRkZT","alg":"HS256"}  ; <DYNAMIC OBJECT>
privateJWKS="{""keys"":[{""kty"":""oct"",""k"":""MTIxMkFTRCFAIyEjQCRAI0AkJCNTREZER0QjJStfKSgqQCRTRkZT"",""alg"":""HS256""}]}"
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsIngtYyI6InRlIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.PcCs_I8AVy5HsLu-s6kQYWaGvuwqwPAElIad11NpM_E

Encryptage asymétrique

L'encryptage asymétrique fait référence à l'utilisation d'une paire de clés : une clé pour signer le jeton (clé privée) et l'autre pour le vérifier (clé publique). Il diffère de l'encryptage symétrique

Clé privée : cette clé est utilisée pour signer le jeton JWT. Elle est gardée secrète et ne doit jamais être exposée.
Clé publique : Cette clé est utilisée pour vérifier l'authenticité du JWT. Elle peut être partagée et distribuée en toute sécurité car elle ne peut pas être utilisée pour signer de nouveaux jetons.

Vous pouvez générer l'encryptage asymétrique JWT avec une clé/un certificat privé via %SYS.X509Credentials. Vous devez donc stocker votre certificat dans cette classe persistante.

 
AsymmetricWithx509

JWT dans les applications Web.

À partir de la version de 2023 , IRIS inclut par défaut la création de JWT intégrée pour les applications Web. Assurez-vous que l'authentification JWT est activée lors de la configuration de votre application Web

J'ai ajouté une brève explication  sur la configuration

1. Activez l' Authentication JWTdans votre application Web
2. Créez une classe REST si vous ne l'avez pas déjà fait
3. La ressource endpoint par défaut « /login » est incluse. Effectuez un appel API REST en utilisant l'authentification de base avec le payload comme {"user": "_SYSTEM", "password": "SYS"}.
4. La réponse sera un JSON contenant le "access_token," "refresh_token," et d'autres détails pertinents.
5. Utilisez le token d'accès pour l'autorisation.

0
1 42
Article Guillaume Rongier · Avr 2, 2025 22m read

L'API REST avec Swagger dans InterSystems IRIS

Salut

Le protocole HTTP permet de récupérer des ressources, telles que des documents HTML. Il est à la base de tout échange de données sur le Web et constitue un protocole client-serveur, ce qui signifie que les requêtes sont initiées par le destinataire, généralement un navigateur Web.

Les API REST tirent parti de ce protocole pour échanger des messages entre le client et le serveur. Cela rend les API REST rapides, légères et flexibles. Les API REST utilisent les verbes HTTP GET, POST, PUT, DELETE et d'autres pour indiquer les actions qu'elles veulent effectuer.

Lorsque nous appelons une API REST, il s'agit en réalité d'un appel HTTP. L'API reçoit cet appel et, conformément au verbe et au chemin demandés, elle effectue l'action souhaitée. Dans le cas de l'implémentation d'Iris, nous pouvons le voir clairement dans la zone de définition de l'URLMap:

XData UrlMap
{
<Routes>
        <Route Url="/cliente" Method="POST" Call="Incluir"  Cors="true"/>
        <Route Url="/cliente/:chave" Method="PUT" Call="Alterar"  Cors="true"/>
        <Route Url="/cliente/:chave" Method="DELETE" Call="Deletar"  Cors="true"/>
        <Route Url="/cliente/:chave" Method="GET" Call="Pesquisar"  Cors="true"/>
        <Route Url="/cliente" Method="GET" Call="Listar"  Cors="true"/>
        <Route Url="/openapi" Method="GET" Call="OpenAPI" Cors="true"/>
        <Route Url="/test" Method="GET" Call="Test" Cors="true"/>
    </Routes>
}

Notez que nous avons le chemin (Url) et le verbe (Method) définis pour chaque appel (Call). Ainsi, le code qui rencontre l'API sait ce qu'il doit faire.

Cette structure de l'API REST ne sert pas seulement à acheminer les actions qui arrivent à l'API.

Elle sert également de base à la création du fichier Swagger de l'API, comme on peut le voir dans la documentation de la classe %REST.API,méthode GetWebRESTApplication: https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25REST.API#GetWebRESTApplication

Examinons maintenant comment générer cette documentation.

Commençons par publier notre API. Utilisons la même API que dans l'article https://community.intersystems.com/post/using-rest-api-flask-and-iam-intersystems-iris-part-1-rest-api

Il suffit de suivre les instructions et d'utiliser le code que l'article fournit pour avoir notre API publiée.

Une fois l'API publiée, nous inclurons un nouvel chemin dans URLMap et une nouvelle méthode dans notre code:

<Route Url="/openapi" Method="GET" Call="OpenAPI" Cors="true"/>

ClassMethod OpenAPI() As %Status
{
              Do ##class(%REST.Impl).%SetContentType("application/json")
    
    Set sc = ##class(%REST.API).GetWebRESTApplication("", "/rest/servico", .swagger)
    
    If $$$ISERR(sc) {
        Quit sc  // If an error occurred, exit the method
    }
   
    Write swagger.%ToJSON()
    Quit $$$OK
}

Incluez le nouvel chemin et la méthode qui lui est associée dans votre code API. Passons maintenant au test. Ouvrons Postman et appelons l'endpoint de génération de Swagger, qui est /openapi:

Ci-dessous, nous avons la définition complète du Swagger généré par notre appel:

{

"info": {

"title": "",

"description": "",

"version": "",

"x-ISC_Namespace": "DEMO"

},

"basePath": "/rest/servico",

"paths": {

"/customer": {

"post": {

"parameters": [

{

"name": "payloadBody",

"in": "body",

"description": "Request body contents",

 "required": false,

"schema": {

"type": "string"

}

}

],

"operationId": "Include",

"x-ISC_ServiceMethod": "Include",

 "x-ISC_CORS": true,

"responses": {

"default": {

"description": "(Unexpected Error)"

},

"200": {

"description": "(Expected Result)"

}

}

},

"get": {

"operationId": "List",

"x-ISC_ServiceMethod": "List",

 "x-ISC_CORS": true,

"responses": {

"default": {

"description": "(Unexpected Error)"

},

"200": {

"description": "(Expected Result)"

}

}

}

},

"/customer/{key}": {

"put": {

"parameters": [

{

"name": "key",

"in": "path",

 "required": true,

"type": "string"

},

{

"name": "payloadBody",

"in": "body",

"description": "Request body contents",

 "required": false,

"schema": {

"type": "string"

}

}

],

"operationId": "Change",

"x-ISC_ServiceMethod": "Change",

 "x-ISC_CORS": true,

"responses": {

"default": {

"description": "(Unexpected Error)"

},

"200": {

"description": "(Expected Result)"

}

}

},

"delete": {

"parameters": [

{

"name": "key",

"in": "path",

 "required": true,

"type": "string"

}

],

"operationId": "Delete",

"x-ISC_ServiceMethod": "Delete",

 "x-ISC_CORS": true,

"responses": {

"default": {

"description": "(Unexpected Error)"

},

"200": {

"description": "(Expected Result)"

}

}

},

"get": {

"parameters": [

{

"name": "key",

"in": "path",

 "required": true,

"type": "string"

}

],

"operationId": "Search",

"x-ISC_ServiceMethod": "Search",

 "x-ISC_CORS": true,

"responses": {

"default": {

"description": "(Unexpected Error)"

},

"200": {

"description": "(Expected Result)"

}

}

}

},

"/openapi": {

"get": {

"operationId": "OpenAPI",

"x-ISC_ServiceMethod": "OpenAPI",

 "x-ISC_CORS": true,

"responses": {

"default": {

"description": "(Unexpected Error)"

},

"200": {

"description": "(Expected Result)"

}

}

}

},

"/test": {

"get": {

"operationId": "Test",

"x-ISC_ServiceMethod": "Test",

 "x-ISC_CORS": true,

"responses": {

"default": {

"description": "(Unexpected Error)"

},

"200": {

"description": "(Expected Result)"

}

}

}

}

},

"Swagger": "2.0"

}

Le lien suivant nous permet d'accéder à la documentation d'Iris qui pointe vers un outil qui reçoit le résultat de notre appel API et le transforme en une interface conviviale pour la documentation et le test du service: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GREST_discover_doc#GREST_gendoc

Exécutons cette URL et fournissons le chemin pour récupérer la définition Swagger de notre API: https://swagger.io/tools/swagger-ui/

Remplaçons l'appel de démonstration sur la page (l'appel petstore) par notre appel: http://127.0.0.1/iris_iss/rest/servico/openapi et voyons l'écran avec la documentation Swagger générée:

En haut de l'appel, nous avons les informations de base de notre API:

Nous pouvons également naviguer dans les appels et consulter des informations importantes, comme dans l'appel POST pour inclure un nouvel utilisateur:

Mais, comme nous l'avons vu un peu plus haut, la définition Swagger est en réalité un fichier au format JSON  qui nous est disponible sous la forme d'un %DynamicObject (voir la documentation dans https://docs.intersystems.com/irislatest/csp/documatic/%25CSP.Documatic.cls?LIBRARY=%25SYS&CLASSNAME=%25Library.DynamicObject) après le ##class(%REST.API).GetWebRESTApplication() que nous effectuons dans notre méthode OpenAPI. De cette façon, en nous basant sur la définition de l'OpenAPI 2.0, nous pouvons inclure ou supprimer des renseignements de notre Swagger. Enrichissons, par exemple, les renseignements de base de l'API. Selon la définition de la version 2.0 (https://swagger.io/specification/v2/ ), les informations suivantes peuvent être mises à disposition:

Complétons alors les informations que nous pouvons rendre disponibles. Pour ce faire, nous allons modifier notre méthode OpenAPI, en incluant les informations de base, le protocole accepté, l'authentification et les données d'accès (hôte et basePath):

ClassMethod OpenAPI() As %Status
{
     Do ##class(%REST.Impl).%SetContentType("application/json")
    
    Set sc = ##class(%REST.API).GetWebRESTApplication("", "/rest/servico", .swagger)
    
    If $$$ISERR(sc) {
        Quit sc  // If an error occurred, exit the method
    }
    
    Set info = {
        "title": "Cliente",
        "description": "API de Manipulação de Cliente",
        "version": "1.00"
    }
    Do swagger.%Set("info", info)
    
    Do swagger.%Set("host", "127.0.0.1")
    Do swagger.%Set("basePath", "/iris_iss/rest/servico")
    Set schemes = []
    Do schemes.%Push("http")
    Do swagger.%Set("schemes", schemes)
             
    Set security = [{"basicAuth":[]}]
    Do swagger.%Set("security", security)
    Set securityDefinitions = {}
    Do securityDefinitions.%Set("basicAuth", {})
    Do securityDefinitions.basicAuth.%Set("type", "basic")
    Do swagger.%Set("securityDefinitions", securityDefinitions)
   
    Write swagger.%ToJSON()
    Quit $$$OK
}


Si nous appelons à nouveau la documentation Swagger sur la page d'aperçu, nous avons maintenant le résultat suivant:

Veillez à ce que notre documentation soit beaucoup plus complète, avec des informations plus détaillées sur l'API, telles que le mécanisme d'authentification (Basic Auth), le protocole accepté (HTTP) et les définitions de version, de description et d'URL.

Nous pouvons désormais enrichir la définition des appels en mettant la structure attendue dans les payloads et les exemples de données pour l'appel. Pour ce faire, superposons les informations dans les chemins pour "/client":

ClassMethod OpenAPI() As %Status
{
    Do ##class(%REST.Impl).%SetContentType("application/json")
    
    Set sc = ##class(%REST.API).GetWebRESTApplication("", "/rest/servico", .swagger)
    
    If $$$ISERR(sc) {
        Quit sc  // If an error occurred, exit the method
    }
    
    Set info = {
        "title": "Cliente",
        "description": "API de Manipulação de Cliente",
        "version": "1.00"
    }
    Do swagger.%Set("info", info)
    
    Do swagger.%Set("host", "127.0.0.1")
    Do swagger.%Set("basePath", "/iris_iss/rest/servico")
    Set schemes = []
    Do schemes.%Push("http")
    Do swagger.%Set("schemes", schemes)
             
    Set security = [{"basicAuth":[]}]
    Do swagger.%Set("security", security)
    Set securityDefinitions = {}
    Do securityDefinitions.%Set("basicAuth", {})
    Do securityDefinitions.basicAuth.%Set("type", "basic")
    Do swagger.%Set("securityDefinitions", securityDefinitions)
   
    Set incluirPesquisar = {
        "post": {
            "summary": "Criar um novo cliente",
            "description": "Recebe os dados de um cliente e cadastra no sistema.",
            "parameters": [
                {
                    "name": "body",
                    "in": "body",
                    "required": true,
                    "schema": {
                        "type": "object",
                        "properties": {
                            "nome": {
                                "type": "string",
                                "description": "Nome do cliente"
                            },
                            "idade": {
                                "type": "integer",
                                "description": "Idade do cliente"
                            }
                        },                              
                        "required": ["nome", "idade"],
                                           "example": {
                                                          "nome": "João Silva",
                                                          "idade": 30
                                           }
                    }
                }
            ],
            "responses": {
                          "default": {
                    "description": "Falha na chamada da API"
                },
                "200": {
                    "description": "Cliente criado com sucesso"
                },
                "406": {
                    "description": "Falha na inclusão do cliente"
                }                
            }
                            },
              "get": {
                    "summary": "Recupera todos os clientes",
                    "description": "Retorna a lista de clientes com suas informações.",
                    "responses": {
                                  "default": {
                    "description": "Falha na chamada da API"
                },
                      "200": {
                        "description": "Lista de clientes",
                        "schema": {
                          "type": "object",
                          "properties": {
                            "clientes": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "chave": {
                                    "type": "string",
                                    "description": "Chave única do cliente"
                                  },
                                  "nome": {
                                    "type": "string",
                                    "description": "Nome do cliente"
                                  },
                                  "idade": {
                                    "type": "integer",
                                    "description": "Idade do cliente"
                                  }
                                },
                                "required": ["chave", "nome", "idade"]
                              }
                            }
                          },
                          "example": {
                            "clientes": [
                              {
                                "chave": "1",
                                "nome": "Maria",
                                "idade": 35
                              },
                              {
                                "chave": "2",
                                "nome": "Julio",
                                "idade": 57
                              },
                              {
                                "chave": "4",
                                "nome": "Julia",
                                "idade": 25
                              },
                              {
                                "chave": "5",
                                "nome": "Julia",
                                "idade": 22
                              }
                            ]
                          }
                        }
                      }
                    }      
              }            
      }
    Do swagger.paths.%Set("/cliente", incluirPesquisar)
   
    Write swagger.%ToJSON()
    Quit $$$OK
}

En rappelant la page de documentation, nous pouvons maintenant voir les méthodes POST  pour inclure un client, et GET pour récupérer une liste:

Notez que nous avons déjà une explication de chacune des méthodes disponibles pour POST et GET qui ont été remplacées.

Notez que nous n'avons plus non plus les balises "x-ISC_ServiceMethod" and "x-ISC_Namespace" qui n'ont pas été incluses dans notre nouvelle définition.

En cliquant pour développer la méthode POST par exemple, nous disposons maintenant visuellement d'un exemple d'appel (Valeur d'exemple):

Et le format du payload de l'appel (Modèle):

Dans le cas de la récupération pour les utilisateurs, nous avons la définition de la réponse renvoyée avec un exemple:

Ainsi qu'avec le modèle prévu comme réponse:

Nous pouvons également tester les appels API à partir de la page de documentation elle-même. Pour cela, le bouton Try it out” (Essayez-le)  doit être disponible sur la page. Appuyez-le , puis appuyez sur Execute et vous verrez le résultat:

Les possibilités de l'approche Code-first  d'une API REST ouvrent de nombreuses possibilités de documentation, nous permettant de contrôler ce qui sera diffusé, en ajoutant ou en supprimant les informations que nous jugeons utiles dans le matériel publié.

La définition de l'OpenAPI est très complète et détaillée. Il existe une variété d'informations que nous pouvons inclure ou retirer, selon nos besoins ou ce que nous voulons y rendre disponible.

À très bientôt!

0
1 46
Article Guillaume Rongier · Mars 26, 2025 7m read

Introduction

Une API REST (Representational State Transfer) est une interface qui permet de faire communiquer différentes applications entre elles via le protocole HTTP, en utilisant des opérations standard telles que GET, POST, PUT, et DELETE. Les API REST sont largement utilisées dans le développement de logiciels pour exposer des services accessibles par d'autres applications, permettant ainsi l'intégration entre différents systèmes.

Cependant, pour garantir que les APIs soient faciles à comprendre et à utiliser, une bonne documentation est essentielle. C'est là qu'OpenAPI entre en jeu.

OpenAPI est une norme de description pour les APIs RESTful. Elle permet de définir de manière structurée les fonctionnalités d'une API, en spécifiant les extrémités disponibles, les types de données acceptés et renvoyés, les paramètres requis et les réponses attendues. Toutes ces informations sont rassemblées dans un fichier de spécification (généralement avec une extension .yaml ou .json), qui peut être interprété par des outils automatisés pour générer du code, de la documentation, etc.

La spécification OpenAPI Specification est conçue pour être lisible par les machines et les humains, permettant la description, la production, la consommation et la visualisation de services web RESTful de manière standardisée. Par conséquent, un document OpenAPI représente une description formelle de l'API, utile à la fois pour les développeurs qui ont besoin de l'utiliser et pour les outils qui peuvent l'exploiter pour automatiser divers processus.

Pourquoi est-il utile de définir un fichier de spécifications?

L'adoption d'OpenAPI pour documenter une API offre plusieurs avantages, notamment:

  • Clarté: elle fournit une documentation détaillée et structurée, permettant aux développeurs de comprendre rapidement comment interagir avec l'API, quelles requêtes doivent être envoyées et quelles données peuvent être attendues en réponse.
  • Automatisation: la documentation peut être générée automatiquement à partir du code, et rester à jour avec tout changement d'API.
  • Interactivité: des outils tels que Swagger, une suite open source de documentation et de test d'API, incluent Swagger UI. Cela vous permet d'explorer et de tester les API directement depuis le navigateur, ce qui simplifie le développement, la vérification et la compréhension des API.
  • Normalisation: l'utilisation d'OpenAPI garantit que la documentation suit un format commun et reconnu, ce qui facilite l'intégration avec d'autres outils et services.

Comment créer un document OpenAPI?

Il existe deux approches principales pour générer un fichier de spécification OpenAPI:

  • Approche "code-first" (automatique): si une API REST a déjà été développée dans InterSystems IRIS, vous pouvez générer automatiquement la documentation OpenAPI sans écrire de fichier de spécification manuellement.
  • Approche "specification-first" (manuelle): dans ce cas, le fichier OpenAPI est écrit manuellement en YAML ou JSON, décrivant tous les points de terminaison, paramètres et réponses attendues. Cette approche est utile lorsque vous souhaitez définir l'API avant de la mettre en œuvre, ce qui facilite la conception et le partage avec d'autres développeurs ou parties prenantes.

Approche automatique

Il existe deux façons de générer automatiquement le fichier de spécification OpenAPI dans InterSystems IRIS.

Méthode 1 : Utilisation de la fonction GetWebRESTApplication

Une approche consiste à utiliser la fonction GetWebRESTApplication fournie par la classe %REST.API.
Un exemple pratique d'utilisation consiste à ajouter la fonction suivante dans la classe de répartition:

ClassMethod GenerateOpenAPI() As%Status
{
    // Le nom de l'application RESTSet webApplication = "MyAPP"// Remplacez par le nom de votre application web// Récupérez la documentation OpenAPI 2.0Set sc = ##class(%REST.API).GetWebRESTApplication("", webApplication, .swagger)
<span class="hljs-keyword">If</span> <span class="hljs-built_in">$$$ISERR</span>(sc) {
    <span class="hljs-keyword">Quit</span> sc  <span class="hljs-comment">// Si une erreur s'est produite, quittez la méthode</span>
}

<span class="hljs-comment">// Renvoyez la documentation au format JSON</span>
<span class="hljs-keyword">Set</span> <span class="hljs-built_in">%response.ContentType</span> = <span class="hljs-string">"application/json"</span>
<span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(OMRREST.impl).<span class="hljs-built_in">%WriteResponse</span>(swagger.<span class="hljs-built_in">%ToJSON</span>()) 

<span class="hljs-keyword">Quit</span> <span class="hljs-built_in">$$$OK</span>

}

Ajoutez en outre le chemin d'accès suivant à l'UrlMap:

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

À ce stade, vous aurez tout ce qu'il vous faut pour générer le fichier de spécification à partir de votre classe d'envoi. Pour consulter la documentation, connectez-vous à l'URL indiquée (où MyWebapp est le nom de votre application web, tel que défini dans le portail de gestion):

<host>:<port>/MyWebapp/openapi

Le JSON ainsi généré représente la spécification OpenAPI de votre API. Après avoir exploré la deuxième méthode, nous verrons comment la visualiser et la tester dans Swagger.

Méthode 2 : Utilisation de l'API de gestion

Une autre façon de générer le fichier de spécification OpenAPI consiste à utiliser l' API de gestion InterSystems IRIS.

Pour appeler ce service, vous pouvez utiliser des outils tels que Postman, un outil de développement qui vous permet de tester, documenter et automatiser les API.
Postman fournit une interface simple et intuitive pour l'envoi de requêtes HTTP (GET, POST, PUT, DELETE, etc.), la visualisation des réponses, la gestion de l'authentification et la création de tests automatisés.

Pour formuler la requête à l'aide de Postman, procédez comme suit:

  1. Cliquez sur le bouton New  et créez une requête HTTP.
  2. Configurez la requête comme suit et envoyez-la:
    • Sélectionnez la méthode GET comme méthode HTTP.
    • Spécifiez l'URL dans le format suivant, en utilisant l' <baseURL> de votre instance:
      https://<baseURL>/api/mgmnt/v1/namespace/myapp
      Ici, namespace est le nom de l'espace de noms où vous avez créé d le service REST, et myapp et le nom de votre application.
    • Définissez la méthode d' Authorisation sur Basic Auth et fournissez le nom d'utilisateur et le mot de passe d'un utilisateur ayant un accès en lecture à l'espace de noms spécifié.

Une fois le JSON généré, il peut être visualisé et testé à l'aide d'outils tels que 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"
}

Approche manuelle

L'approche manuelle pour générer un document OpenAPI consiste à écrire manuellement le fichier de spécification au format YAML ou JSON. Cette approche est particulièrement utile lorsque vous souhaitez contrôler entièrement la conception de l'API avant sa mise en œuvre, ou lorsque vous documentez une API déjà existante sans recourir à des outils automatisés.

Pour rédiger le fichier de spécification OpenAPI, vous pouvez vous référer à , la documentation officielle de la version 2.0 de la spécification OpenAPI, où vous trouverez des renseignements sur les champs obligatoires et sur la manière de décrire les points de terminaison, les paramètres, les réponses, etc. Ce guide détaillé vous aidera à comprendre comment structurer correctement le fichier YAML ou JSON afin de respecter les normes OpenAPI.

Un bon exemple d'utilisation de cette approche est la création d'un service REST à l'aide des méthodes décrites dans la documentation officielle d'InterSystems IRIS.
Vous trouverez une introduction au développement et à la configuration d'une application REST dans IRIS sur cette page de la documentation, qui décrit étape par étape les méthodes nécessaires pour exposer une application RESTful avec IRIS.

2
1 85
Article Stav Bendarsky · Mars 4, 2025 6m read

La surveillance de votre déploiement IRIS est cruciale. Avec l'obsolescence de System Alert and Monitoring (SAM), une solution moderne et scalable est nécessaire pour obtenir des informations en temps réel, détecter précocement les problèmes et améliorer l'efficacité opérationnelle. Ce guide explique comment configurer Prometheus et Grafana dans Kubernetes pour surveiller efficacement InterSystems IRIS.

Ce guide suppose que vous avez déjà déployé un cluster IRIS en utilisant l'InterSystems Kubernetes Operator (IKO), qui simplifie le déploiement, l'intégration et la gestion.

 

0
0 65
Question Anthony Decorte · Fév 5, 2025

Bonjour, j'ai un problème avec un appel et j'aurais besoin d'aide.

Lorsque j'appelle une API avec la méthode SendFormDataArray de l'adaptateur EnsLib.HTTP.OutboundAdapter, je lui passe un objet %Net.HttpRequest et je reçois l'erreur suivante :

ERROR #5002: ObjectScript error: <SUBSCRIPT>MatchSuffix+1^%Net.HttpRequest.1 ^%qPublicSuffix("")

J'ai correctement saisi l'url dans mon business operation et je la passe dans le dernier paramètre de la méthode SendFormDataArray. Avez-vous une idée, s'il vous plaît ?

0
0 46
Article Iryna Mykhailova · Jan 27, 2025 1m read

Dans votre production d'interopérabilité, vous pouvez toujours avoir une Business Operation qui est un client HTTP, qui utilise OAuth 2.0 pour l'authentification, mais vous avez du personnaliser l'opération pour cette méthodologie d'authentification. Depuis la version v2024.3, qui a été récemment publiée, il existe une nouvelle fonctionnalité, fournissant de nouveaux paramètres, pour gérer cela plus facilement.

Dans votre Business Operation qui utilise l'outbound adaptateur HTTP, vous trouverez de nouveaux paramètres, sous le groupe OAuth.

0
0 42
Article Guillaume Rongier · Jan 24, 2025 6m read

Dans le cadre du concours Open Exchange, l'hôpital Salford Royal (Dean White et Mark O'Reilly) a développé une API REST pour SharePoint, un modèle qui fonctionne mais qui peut aussi servir de point de départ à vos propres applications REST 

Conditions préalables

Lorsque vous utilisez la v1 du service REST de l'API de Sharepoint, vous avez besoin d'un identifiant locataire, d'un identifiant client, d'un code secret client et d'un nom de locataire 

Configuration 

Configuration d'un serveur OAuth

 

Le code au milieu est l'identifiant locataire 

Créez un nom de config client comme vous le souhaitez 

Configurer votre client oauth en remplaçant l'adresse IP de votre serveur par l'adresse IP du serveur sur lequel vous vous trouvez (pas l'adresse VIP - si vous ne faites pas partie d'un VIP, l'adresse locale peut fonctionner) 

Ajouter les informations d'identification client 

modifiez les paramètres sur SharepointRESTConnector comme HTTPSERVER,SHAREPOINT-SITENAME- SHAREPOINT FILEPATH- SSL (vierge jusqu'à la v. 1.3) les paramètres remplacent le nom locataire et l'identifiant locataire. 

Code 

SharePointOnlineRESTOperation

OAuth Scope n'est pas utilisateur dans cet exemple  mais est laissé ici comme modèle si vous en avez besoin pour d'autres implémentations rest 

Il utilise et s'appuie sur rest par défaut   Set tSC=..AddAccessToken(.tHttpRequest) qui gère le jeton et transmet toutes les propriétés supplémentaires requises pour l'API. Pour l'API SharePoint, une ressource est nécessaire et cette dernière est ajoutée dans les paramètres dans les notes de commentaires 

/// Pour SPO, les paramètres doivent être {"resource":"00000003-0000-0ff1-ce00-000000000000/{TennantName}.sharepoint.com@{TennantID}"} <p>/// 00000003-0000-0ff1-ce00-000000000000 est l'identifiant ResourceID attribué à SPO par Microsoft et ne doit pas être modifié <p>/// {TennantName} doit être modifié pour être identique à celui du serveur HTTP, par exemple intersystems.sharepoint.com <p>/// {TennantID} est l'identifiant de votre nom de serveur 

Obtention d'une liste de fichiers 

Appelle la liste des fichiers dans le répertoire dont vous disposez. Cette fonction peut indiquer le temps écoulé depuis le dernier téléchargement ou la durée totale des fichiers Elle interroge l'en-tête

Elle appelle GetFolderByServerRealativeURL

Set..Adapter.URL="/sites/"_$$$URLENCODE(..SharepointSitename)_"/_api/web/GetFolderByServerRelativeUrl('"_$$$URLENCODE(..SharepointFilePath)_"')/Files"_filter Set..Adapter.URL="/sites/"_$$$URLENCODE(..SharepointSitename)_"/_api/web/GetFolderByServerRelativeUrl('"_$$$URLENCODE(..SharepointFilePath)_"')/Files"_filter

 La réponse est lue par le processeur. 

Il envoie des messages http comme le ferait POSTMAN 

Une méthode de réponse Constuct a été tirée de l'opération générique écrite par intersystems pour renvoyer des réponses http 

Suppression du fichier

Appelle une demande d'envoi de suppression à getfolderbyserverrelativeurl/files getfolderbyserverrelativeurl/files 

lignes clés ci-dessous 

Set..Adapter.URL="/sites/"_$$$URLENCODE(..SharepointSitename)_"/_api/web/GetFolderByServerRelativeUrl('"_$$$URLENCODE(..SharepointFilePath)_"')/Files('"_$$$URLENCODE(pRequest.FileName)_"')"Set tSC=..AddAccessToken(.tHttpRequest)
  	s tSC = ..SendRequest(.tHttpResponse,send,tHttpRequest, .pResponse)
    Quit..constructResponse(.tHttpResponse,.pResponse)

Téléchargement du fichier

Si c'est un Ens.StringContainer (vous pourriez en faire un message sur mesure qui l'étendrait, comme Messages.DownloadSharpointFile), il lit le nom et l'envoie dans l'url de l'api. Il lit le paquet de réponses et ajoute le flux binaire à un steamcontainer. Comme toujours, il faut créer le flux et l'empaqueter dans le streamcontainer. 

Code clé ci-dessous (quelques s ont été modifiés pour être affichés ici) 

set binaryStream =##Class(%Stream.FileBinary).%New()
  Set tSC=..AddAccessToken(.tHttpRequest)
  Set..Adapter.URL="/sites/"_$$$URLENCODE(..SharepointSitename)_"/_api/web/GetFolderByServerRelativeUrl('"_$$$URLENCODE(..SharepointFilePath)_"')/Files('"_$$$URLENCODE(pRequest.StringValue)_"')/OpenBinaryStream()"Set tHttpResponse = ##class(%Net.HttpResponse).%New()
  set send="GET"set tSC = ..SendRequest(.tHttpResponse,send,tHttpRequest, .pResponse)
  set pDownloadResponse =##Class(Ens.StreamContainer).%New(binaryStream)
  set pDownloadResponse.OriginalFilename=pRequest.StringValue
	

Ajout du fichier

GetFolderByServerRelativeUrl/filepath/Files/add(url=filename,overwrite?)

Lignes clées 

Set..Adapter.URL="/sites/"_$$$URLENCODE(..SharepointSitename)_"/_api/web/GetFolderByServerRelativeUrl('"_$$$URLENCODE(..SharepointFilePath)_"')/Files/add(url='"_fn_"',overwrite="_$$$URLENCODE(..OverwriteExistingFile)_")"Set tSC=..AddAccessToken(.tHttpRequest)
  s tHttpRequest.EntityBody=##Class(%Stream.FileBinary).%New()
	s sc=tHttpRequest.EntityBody.CopyFromAndSave(pFileToUpload.Stream)
	Set tHttpResponse = ##class(%Net.HttpResponse).%New()
	S send="POST"s tSC = ..SendRequest(.tHttpResponse,send,tHttpRequest, .pResponse)

Envoi de la demande 

Cette fonction permet d'envoyer toute requête qui attend une réponse http. 

Assure le transfert des réponses et l'envoi de ENSLIB.HTTP.GenericMessage. De nombreux en-têtes sont renvoyés et une case à cocher permet de simplifier la réponse pour qu'elle ne contienne qu'un code d'erreur et des données. 

Construction de la réponse

Utilisé ailleurs dans l'EIT et non dans le code original de cette méthode

AddAccessToken

C'était le véritable apprentissage. il s'agit d'un code par défaut pour utiliser les paramètres OAuth Intersystems et non pas un code en dur à chaque fois que nous avons besoin de l'utiliser. 

Tout est construit autour de trois appels 

est autorisé et 

Set isAuthorised = ##class(%SYS.OAuth2.AccessToken).IsAuthorized(..OAuthClientApplicationName,sessionId,..OAuthScope,.accessToken,,.responseProperties,.error)

 Obtenez un jeton d'accès

Set tSC = ##class(%SYS.OAuth2.Authorization).GetAccessTokenClient(..OAuthClientApplicationName,..OAuthScope,.properties,.error,.sessionId)

et un jeton d'ajout qui l'ajoute à l'en-tête - malheureusement, il ne semble pas qu'il puisse l'ajouter au corps du message si l'identifiant est requis par d'autres API

;La valeur par défaut de sslConfiguration provient de l'instance OAuth2.Client.        Set tSC  = ##class(%SYS.OAuth2.AccessToken).AddAccessToken(pHttpRequest,sendType,,..OAuthClientApplicationName,sessionId)

En outre, l'API Sharepoint nécessite une ressource. Nous avons généralisé l'utilisation de JSON, et si vous avez besoin d'autres paramètres, nous avons pensé à les ajouter en JSON afin de pouvoir réutiliser le modèle à l'avenir.

il l'ajoute à l'objet chaîne de caractères utilisé par les propriétés. il s'agit d'une chaîne de caractères sérialisée sous forme de tableau ou similaire 

s paramsarr = [].%FromJSON(..Params)
            s iterator = paramsarr.%GetIterator()
            s properties=""While iterator.%GetNext(.key,.value)
            {
                s properties(key)=value
            }

Exemples de traces

 

Obtention de la liste des fichiers

Téléchargement de fichiers

suppression de fichiers

si vous cochez cette case 

Ajout de fichiers

Remerciements à @Dean White 
 

https://youtu.be/485dTXYp2BU

Mise à jour - ajout d'un lien YouTube et correction du lien d'échange ouvert;

0
0 57
Article Pierre LaFay · Juin 26, 2024 1m read

Ajouter un identifiant pour se connecter à l'interface FHIR REST - dans ce cas, ne considérer qu'une authentification de base

 

Ajouter un registre de service - dans ce cas, ne considérer qu'une authentification de base

- configurer un service HTTP

- saisir le chemin d'accès au serveur FHIR

- saisir l'URL du service FHIR

- utiliser l'identifiant profilé


 

 

Ajouter un "HS.FHIRServer.Interop.HTTPOperation"

Choisissez le nom du service

Tester le client FHIR

Tracer le résultat du test

0
0 66
Article Sylvain Guilbaud · Juin 7, 2024 10m read

Avant-propos

Les versions 2022.2 et ultérieures d'InterSystems IRIS offrent la possibilité de s'authentifier auprès d'une API REST à l'aide de jetons web (JWT) JSON. Cette fonctionnalité renforce la sécurité en limitant le lieu et la fréquence de transfert des mots de passe sur le réseau et en fixant un délai d'expiration pour l'accès.

L'objectif de cet article est de servir de tutoriel sur la façon d'implémenter une API REST fictive en utilisant InterSystems IRIS et de verrouiller l'accès à cette API par le biais de JWT.

REMARQUE Je ne suis PAS développeur. Je ne fais aucune déclaration quant à l'efficacité, l'évolutivité ou la qualité des échantillons de code que j'utilise dans cet article. Les exemples ci-dessous sont donnés UNIQUEMENT à des fins éducatives. Ils ne sont PAS destinés à un code de production.

Prologue

Cette réserve étant faite, explorons les concepts que nous allons décortiquer ici.

Qu'est-ce que REST?

Le terme REST est l'acronyme de REpresentational State Transfer. Il s'agit d'un style d'architecture pour la conception d'applications connectées et pour l'accès à des fonctions publiées par ces applications.

Qu'est-ce qu'un JWT?

Un jeton web JSON (JWT) est un moyen compact et sûr au niveau de l'URL qui permet de représenter les demandes transférées entre deux parties et qui peuvent être signées numériquement, cryptées ou les deux à la fois. Si vous souhaitez en savoir plus sur les JWT et les autres classes web JSON prises en charge par InterSystems IRIS, lisez cet article.

Se salir les mains

Selon les spécifications

Pour consommer une API REST, il faut d'abord disposer d'une API REST. J'ai fourni ici un exemple de spécification OpenAPI 2.0 qui est adapté au jeu de rôle sur table (TTRPG). Je l'utiliserai tout au long des exemples présentés ici. Il existe de nombreux exemples de rédaction en ligne, alors n'hésitez pas à vous y plonger, mais la spécification n'est qu'un plan directeur. Il ne fait rien d'autre que de nous informer sur l'utilisation de l'API.

Génération d'API REST

InterSystems IRIS fournit une manière très soignée de générer des souches de code pour l'API REST. Cette documentation fournit une méthode complète de génération de souches de code. N'hésitez pas à utiliser la spécification OpenAPI 2.0 que j'ai fournie dans la section précédente.

Mise en œuvre

C'est là que nous allons creuser. La section de génération aura créé trois fichiers .cls pour vous :

  1. impl.cls
  2. disp.cls
  3. spec.cls

Nous allons passer le plus de temps possible dans impl.cls, peut-être toucher à disp.cls pour le débogage, et laisser spec.cls tranquille.

Dans impl.cls se trouvent les souches de code pour les méthodes que disp.cls appellera lorsqu'il recevra une requête API. La spécification OpenAPI a défini ces signatures. Elle peut dire ce que vous voulez qu'elle fasse, mais c'est vous qui devez la mettre en œuvre. Alors faisons ça!

Création

L'une des façons dont nous utilisons une base de données est d'y ajouter des objets. Ces objets servent de base à nos autres fonctions. Sans objets existants, nous n'aurons rien à voir, nous allons donc commencer par notre modèle d'objet : un caractère Character !

Un Character aura nécessairement un nom et spécifiera éventuellement sa classe, sa race et son niveau. Voici un exemple d'implémentation de la classe TTRPG.Character.

Class TTRPG.Character Extends %Persistent
{

Property Name As %String [ Required ];

Property Race As %String;

Property Class As %String;

Property Level As %String;

Index IndexName On Name [ IdKey ];

ClassMethod GetCharByName(name As %String) As TTRPG.Character
{
set character = ##class(TTRPG.Character).%OpenId(name)

Quit character
}
}

Puisque nous voulons stocker les objets Character dans la base de données, il nous faut hériter de la classe %Persistent. Nous voulons être capables de rechercher nos charactères par leur nom plutôt que de leur assigner une clé d'identification arbitraire, donc nous définissons l'attribut [ IdKey ] sur l'index pour la propriété Character.Name. Cela garantit également l'unicité du nom du caractère.

Une fois notre modèle d'objet fondamental défini, nous pouvons analyser la mise en œuvre de l'API REST. La première méthode que nous allons explorer est la méthode PostCharacter.

En résumé, cette partie consomme une requête HTTP POST vers le point de terminaison /characters avec les propriétés de caractères que nous avons définies dans le corps de la requête. Il devrait prendre les arguments fournis et créer un objet TTRPG.Character à partir de ceux-ci, le sauvegarder dans la base de données, et nous faire savoir s'il a réussi ou non.

ClassMethod PostCharacter(name As %String, class As %String, race As %String, level As %String) As %DynamicObject
{
set results = {} // create the return %DynamicObject

//créer l'objet caractère
set char = ##class(TTRPG.Character).%New()

set char.Name = name
set char.Class = class
set char.Race = race
set char.Level = level
set st = char.%Save()

if st {
set charInfo = {}
set charInfo.Name = char.Name
set charInfo.Class = char.Class
set charInfo.Race = char.Race
set charInfo.Level = char.Level
set results.Character = charInfo
Set results.Status = "success"
}
else {
Set results.Status = "error"
Set results.Message = "Unable to create the character"
}
Quit results
}

Désormais, nous pouvons créer des caractères, mais comment récupérer celui que nous venons de créer ? Selon la spécification OpenAPI, le point de terminaison /characters/{charName} nous permet de récupérer un caractère par son nom. Nous récupérons l'instance de caractère, si elle existe. S'il n'existe pas, nous renvoyons une erreur indiquant à l'utilisateur qu'un caractère avec le nom fourni n'existe pas. Ceci est mis en œuvre dans la méthode GetCharacterByName.

ClassMethod GetCharacterByName(charName As %String) As %DynamicObject
{
// Créer un nouvel objet dynamique pour stocker les résultats
Définir les résultats = {}

set char = ##class(TTRPG.Character).GetCharByName(charName)

if char {
set charInfo = {}
set charInfo.Name = char.Name
set charInfo.Class = char.Class
set charInfo.Race = char.Race
set charInfo.Level = char.Level
set results.Character = charInfo
Set results.Status = "success"
}
// Si aucun caractère n'a été trouvé, un message d'erreur est affiché dans l'objet de résultats.
else {
Set results.Status = "error"
Set results.Message = "No characters found"
}

// Renvoyer l'objet de résultats
Quit results
}

Mais c'est juste votre caractère. Qu'en est-il de tous les autres caractères créés par d'autres personnes ? Nous pouvons visualiser ces caractères en utilisant la méthode GetCharacterList. Il consomme une requête HTTP GET vers le point de terminaison /characters pour compiler et renvoyer une liste de tous les caractères de la base de données.

ClassMethod GetCharacterList() As %DynamicObject
{
// Créer un nouvel objet dynamique pour stocker les résultats
Définir les résultats = {}
set query = "SELECT Name, Class, Race, ""Level"" FROM TTRPG.""Character"""
set tStatement = ##class(%SQL.Statement).%New()
set qstatus = tStatement.%Prepare(query)
if qstatus '= 1 { Do ##class(TTRPG.impl).%WriteResponse("Error: " _ $SYSTEM.Status.DisplayError(qstatus)) }
set rset = tStatement.%Execute()
Set characterList = []
while rset.%Next(){
Set characterInfo = {}
Set characterInfo.Name = rset.Name
set characterInfo.Race = rset.Race
Set characterInfo.Class = rset.Class
Set characterInfo.Level = rset.Level

Do characterList.%Push(characterInfo)

}
if (rset.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rset.%SQLCODE, ": ", rset.%Message quit}

set totalCount = rset.%ROWCOUNT

// Définit les propriétés status, totalCount et characterList dans l'objet de résultats
Set results.Status = "success"
Set results.TotalCount = totalCount
Set results.CharacterList = characterList


// Renvoyer l'objet de résultats
Quit results
}

C'est notre API! La spécification actuelle ne fournit pas de moyen de mettre à jour ou de supprimer des caractères de la base de données, et cela reste un exercice pour le lecteur!

Configuration IRIS

Maintenant que notre API REST est implémentée, comment pouvons-nous la faire communiquer avec IRIS ? Dans le portail de gestion, si vous allez sur la page Administration du système > Sécurité > Applications > Applications Web, vous pouvez créer une nouvelle application Web. Le nom de l'application est le point de terminaison que vous utiliserez lors de vos requêtes. Par exemple, si vous l'avez nommée /api/TTRPG/, les requêtes pour l'API iront à http://localhost:52773/api/TTRPG/RPG/{endpoint}. Pour une installation locale de sécurité normale d'IRIS, cela ressemble à http://localhost:52773/api/TTRPG/{endpoint}. Donnez-lui une belle description, définissez l'espace de noms souhaité et cliquez sur le bouton radio pour REST. Pour activer l'authentification JWT, cochez la case "Use JWT Authentication". Le délai d'expiration du jeton d'accès JWT détermine la fréquence à laquelle un utilisateur devra recevoir un nouveau JWT. Si vous prévoyez de tester l'API pendant une période prolongée, je vous recommande de fixer cette valeur à une heure (3 600 secondes) et le délai d'expiration du jeton JWT (JWT Refresh Token Timeout) (la période pendant laquelle vous pouvez renouveler votre jeton avant qu'il n'expire définitivement) à 900 secondes.

web app config

Maintenant que l'application est configurée, nous devons configurer IRIS lui-même pour permettre l'authentification JWT. Vous pouvez configurer cette option dans Administration système > Sécurité > Sécurité du système > Authentification/Options de session Web. Le champ de l'émetteur du JWT et l'algorithme de signature à utiliser pour signer et valider les JWT se trouvent en bas de la page. Le champ émetteur apparaît dans la section de demande d'indemnisation du JWT et a pour but d'indiquer qui vous a donné ce jeton. Vous pourriez le définir sur "InterSystems".

JWT authentication config

Temps de test

Tout est configuré et mis en œuvre, alors lançons-nous ! Chargez votre outil favori de création de requêtes API (j'utiliserai une extension Firefox appelée RESTer dans les exemples) et nous allons construire des requêtes API REST.

Tout d'abord, essayons de répertorier tous les caractères qui existent.

list no token

Nous avons reçu une erreur "HTTP 401 Unauthorized". C'est parce que nous ne sommes pas connectés. Vous vous dites peut-être, Elliott, nous n'avons pas implémenté de fonctionnalité de connexion à cette API REST. Ce n'est pas grave, car InterSystems IRIS s'en charge pour nous lorsque nous utilisons l'authentification JWT. Il fournit quatre points de terminaison que nous pouvons utiliser pour gérer notre session. Il s'agit de /login, /logout /revoke et /refresh. Ils peuvent être personnalisés dans le fichier disp.cls comme dans l'exemple ci-dessous :

Parameter TokenLoginEndpoint = "mylogin";
Parameter TokenLogoutEndpoint = "mylogout";
Parameter TokenRevokeEndpoint = "myrevoke";
Parameter TokenRefreshEndpoint = "myrefresh";

Accédons maintenant au point de terminaison /login.

logging in

Le corps de cette demande n'est pas affiché pour des raisons de sécurité, mais il reprend la structure JSON :

{"user":"{YOURUSER}", "password":"{YOURPASSWORD}"}

En échange de notre mot de passe, nous recevons un JWT! C'est la valeur de "access_token". Nous allons copier ceci et l'utiliser dans nos demandes à l'avenir afin de ne pas avoir à transmettre notre mot de passe tout le temps.

Maintenant que nous avons un JWT pour l'authentification, essayons de créer un caractère!

Nous formatons notre demande comme ci-dessous:

character creation

Utilisation du jeton "bearer token" en tant qu'en-tête au format "Authorization: Bearer {JWTValue}". Dans une requête curl, vous pouvez écrire ceci avec -H "Authorization: Bearer {JWTValue}"

Créons un autre caractère pour le plaisir, en utilisant les valeurs de votre choix.

Essayons maintenant de répertorier tous les caractères qui existent dans la base de données.

listing characters

Nous récupérons nos deux caractères que nous avons créés! Mais qu'en est-il si nous voulons simplement accéder à l'un d'entre eux ? Eh bien, nous avons implémenté cela avec le point de terminaison /characters/{charName}. Nous pouvons formater cette requête comme ceci:

retrieving specific character

C'est notre API REST qui est à l'œuvre ! Lorsque vous avez terminé votre session, vous pouvez vous déconnecter au point de terminaison /logout en utilisant votre JWT. Ceci révoquera le JWT et le mettra sur une liste noire afin que vous ne puissiez plus l'utiliser.

Conclusion

Les versions 2022.2+ d'InterSystems IRIS offrent la possibilité de s'authentifier auprès d'une API REST à l'aide de jetons web (JWT) JSON. Cette fonctionnalité améliore la sécurité en limitant l'utilisation des mots de passe et en définissant une date d'expiration pour l'accès à l'API.

J'espère que cette présentation sur la génération d'une API REST et sa sécurisation avec des JWT via IRIS vous a été utile. Faites-moi savoir si c'était le cas! J'apprécie tout commentaire.

0
1 73
Article Iryna Mykhailova · Avr 29, 2024 12m read

Le défi du Lo-Code

Imaginons la scène.  Vous travaillez tranquillement au sein de Widgets Direct, le premier détaillant de Widgets et d'accessoires pour Widgets sur Internet.   Votre patron vous annonce une nouvelle désastreuse : certains clients ne sont peut-être pas satisfaits de leurs widgets et nous avons besoin d'une application d'assistance pour assurer le suivi de ces réclamations.   Pour rendre les choses plus intéressantes, il veut que cette application ait une très faible empreinte de code et vous demande de livrer une application en moins de 150 lignes de code à l'aide d'InterSystems IRIS.  Est-ce possible?

Avertissement : cet article présente la construction d'une application très basique et omet, par souci de concision, des éléments de détail tels que la Sécurité et la Gestion des erreurs.   Cette application ne doit être utilisée qu'à titre de référence ni pour une application de production.  Cet article utilise IRIS 2023.1 comme plate-forme de données, certaines fonctionnalités décrites ne sont pas disponibles dans les versions antérieures

Étape 1 – Définition d'un modèle de données

Nous commençons par définir un nouvel espace de noms propre - avec une base de données de codes et de données. Bien que tout soit regroupé dans une seule base de données, il est utile de diviser ces bases pour permettre l'actualisation des données.

Notre système d'assistance a besoin de 3 classes de base : un objet Ticket qui peut contenir des actions pour documenter les interactions entre un conseiller du personnel UserAccount et un contact client UserAccount.  Définissons ces classes avec quelques propriétés de base:

19 lignes de code – et nous avons notre modèle de données complet!  Nous avons défini 2 classes comme Persistent afin qu'elles puissent être sauvegardées dans la base de données, et nous héritons également de %JSON.Adapter, ce qui nous permet d'importer et d'exporter très facilement nos objets au format JSON.  En guise de test, nous configurons notre premier utilisateur dans Terminal, nous le sauvegardons et nous vérifions que l'application JSONExport fonctionne correctement

Tout cela semble bon.  Le patron nous a laissé un fichier csv avec une liste d'employés et de clients.   Nous pourrions écrire un code pour analyser ce fichier et le charger, mais y a-t-il un moyen plus simple?

Étape 2 – TÉLÉCHARGEMENT DES DONNÉES

InterSystems IRIS fournit une instruction de chargement de données (LOAD DATA) simple à utiliser en SQL qui permet de télécharger facilement des données à partir d'un fichier CSV, y compris les options permettant d'analyser les en-têtes et de renommer les champs.  Utilisons-la pour télécharger notre table d'utilisateurs:

Nous pouvons utiliser les étiquettes d'en-tête pour extraire ces données et les télécharger dans la base de données de la manière suivante:

Les 300 lignes ont été importées en une seule commande.   Les 4 lignes supplémentaires de code portent notre compte courant à 23 lignes de code écrites.   Nous pouvons rapidement vérifier que ces enregistrements sont corrects avec une sélection SQL de base

Nous avons maintenant nos données de départ, construisons donc quelques API de base pour permettre à un front-end d'être connecté.  Nous construirons notre API comme un service REST qui sert et accepte JSON.

Étape 3 – Création d'une API REST

InterSystems IRIS fournit un support REST natif par le biais de l'héritage de la classe %CSP.REST, nous allons donc créer une classe REST.Dispatch et hériter de %CSP.REST.   Cette classe est composée de deux sections: une UrlMap XData qui associe les URL et les Verbes aux méthodes, et les méthodes qui sont appelées par ces Urls.

Notre Produit minimum viable nécessite 4 opérations: la récupération de la liste des utilisateurs pour le personnel ou les clients, la récupération des derniers tickets collectés, la récupération d'un ticket unique par son numéro d'identification, et enfin la création d'un nouveau ticket.   Nous définissons nos verbes, et puis les méthodes.

GetUserList est un Curseur de base SQL intégré qui fournit les données directement en JSON.  Nous pouvons alors analyser ces données avec la fonctionnalité JSON native, les placer dans un tableau JSON et les servir en tant que corps de la réponse.  Nous passons la variable "staff" de l'URL directement à la requête pour modifier le contexte des données.

TicketSummary est presque identique, mais la requête accède alors à la table des tickets

TicketSummary est le service le plus simple.  Nous ouvrons l'objet par ID, et écrivons le %JSONExport intégré à la sortie.  Si l'objet ne se charge pas, alors nous écrivons un paquet d'erreurs

Enfin, notre méthode d'UploadTicket est la plus complexe. Nous devons lire le payload de l'objet de la requête, l'analyser en JSON, puis l'appliquer à une nouvelle instance de Ticket en utilisant %JSONImport.  Nous définissons également l'OpenDate et l'OpenTime à partir de l'heure actuelle, au lieu de les attendre en entrée.  Après une sauvegarde réussie, nous renvoyons la représentation JSON de l'objet, ou si l'objet ne se télécharge pas, nous renvoyons une erreur.

Avec ces services, nous ajoutons 60 lignes de code supplémentaires à notre total.  Nous atteignons maintenant un total de 89 lignes de code pour cette application

Nous devons maintenant créer une application Web sous Sécurité>Applications.  Celle-ci doit être définie comme une application de type REST, et le NOMCLASSE (Classname) doit être défini comme la classe de répartition (Dispatch) que nous venons de créer (Remarque: vous devrez accorder un rôle de sécurité approprié à cette application afin qu'elle puisse accéder au code et aux données).  Après avoir sauvegardé, les services REST peuvent maintenant être appelés à partir de l'URL que vous avez définie

Nous pouvons appeler l'UserList pour vérifier

Nous sommes maintenant prêts à créer des données.  Utilisons notre client REST pour envoyer un payload au service de création de tickets.  Nous fournissons un mot-clé, une description, un conseiller (Advisor) et un contact (Contact), et nous recevons en retour le fichier JSON du ticket que nous avons créé, y compris l'OpenDate et le TicketId

Nous avons maintenant notre Produit minimum viable.  En utilisant un constructeur de formulaire frontal de notre choix, nous pouvons maintenant envoyer et recevoir des informations sur les tickets via nos services REST.

Étape 4 – Exigences d'interopérabilité

Vous avez maintenant construit une application de ticketing de base en seulement 89 lignes de code écrites.   Votre patron doit être impressionné?   Oui, mais il a de mauvaises nouvelles.  Vous avez oublié une exigence.   Widgets Direct a un contrat spécial avec les régions francophones et tous les billets rédigés en français doivent être envoyés à Mme Bettie Francis pour un premier examen.   Heureusement, vous avez un collègue qui a suivi l'excellent article de Robert Luman "Sur la prise en charge du langage naturel par Python" ("Python Natural Language Support") et qui a créé un service REST capable d'accepter un échantillon de texte et d'en identifier la langue.   Pouvons-nous utiliser l'Interopérabilité d'InterSystems IRIS pour appeler ce service et mettre automatiquement à jour le conseiller de Mme Francis lorsque le texte est en français?

Nous devons commencer par la création de Classes de messages afin d'avoir un moyen d'envoyer et de recevoir nos demandes.  Nous avons besoin d'une requête qui contiendra l'identifiant du ticket et le texte de l'échantillon, et d'une réponse qui renverra le Code de la langue et la Description. Ceux-ci hériteront d' Ens. Request et d'Ens. Response

6 autres lignes de code écrites nous permettent d'atteindre 95 LOC.  Nous devons maintenant créer notre opération, qui enverra la requête au service de votre collègue et récupérera la réponse.  Nous définissons une opération Outbound, avec des propriétés pour le serveur et l'URL, et nous les exposons à la configuration de l'utilisateur en les incluant dans le paramètre SETTINGS.   Cela nous permettra de mettre facilement à jour la requête si le chemin d'accès au serveur change.   Nous créons une méthode d'aide pour configurer une requête HTTPRequest, puis nous l'utilisons pour appeler le service et remplir notre réponse

27 lignes de code supplémentaires nous amènent à plus de 100, nous avons maintenant 122 lignes écrites.   Nous devons maintenant configurer cette classe dans notre production Ensemble.  Nous devons maintenant configurer cette classe dans notre production Ensemble. Accédez à la configuration de la production sous Interopérabilité, et cliquez sur Ajouter (Add) sous l'En-tête Opérations (Operations Header).  Configurez votre opération avec le nom de la classe et un nom d'affichage

Nous pouvons ensuite cliquer dessus pour accéder aux paramètres (Settings), entrer le nom du serveur et l'URL et activer l'Opération. 

Nous avons maintenant besoin d'une deuxième opération qui prend l'identifiant d'un ticket et associe le conseiller à l'identifiant d'un compte d'utilisateur fourni.  Nous avons besoin à la fois d'un message et d'une classe d'opération, mais dans ce cas, nous ne renverrons pas de réponse, l'opération exécutera la tâche sans feedback

Les 12 lignes de code supplémentaires nous amènent à 134 lignes écrites.  Nous pouvons ajouter cette Opération à la Production de la même manière que nous avons ajouté le Service linguistique (Language Service), mais dans ce cas, nous n'avons pas de configuration à définir.

Nous avons ensuite besoin d'un routeur capable d'appeler le service, d'évaluer la réponse et, éventuellement, d'appeler l'Opération du conseiller français (French Advisor Operation).  Nous allons vers Interoperability>Build>BusinessProcess et accédons à l'outil de création de règles visuelles.  Nous définissons d'abord nos contextes pour la requête (Request) et la réponse (Response) et nous ajoutons un élément d'appel (Call).  Nous définissons nos entrées et nos sorties sur les classes de messages que nous avons créées, puis nous mappons les entrées à l'aide du générateur de requêtes.  Assurez-vous que l'option "Asynchrone" (Asynchronous) n'est pas cochée, car nous voulons attendre la réponse avant de continuer.

Nous ajoutons ensuite un élément "Si" (If) pour évaluer le code de langue renvoyé.  S'il s'agit de "fr", nous voulons faire appel à l'opération de FrenchAdvisor

Mme Francis est l'utilisateur ID 11, nous configurons donc notre objet d'appel (Call object) pour qu'il fournisse notre message AdvisorUpdate au service FrenchAdvisor, et nous utilisons le constructeur de requêtes pour transmettre le TicketID et une valeur fixe de 11 au paramètre Advisor

Nous pouvons maintenant l'ajouter à la Production en cliquant sur Ajouter (Add) sous l'en-tête Processus, en sélectionnant la classe et en lui donnant un nom d'affichage "FrenchRouter". 

Nous avons maintenant notre routage en place. Nous avons juste besoin d'un service pour rechercher les nouveaux tickets et les envoyer au routeur pour traitement.  Nous définissons une classe de service basée sur un adaptateur SQL de la manière suivante (en ajoutant 8 lignes de code supplémentaires à notre compte):

Nous l'ajoutons ensuite à notre production comme nous l'avons fait avec les objets Operation et Process.  Nous devons configurer l'adaptateur SQL.   Nous fournissons les détails de connexion via un DSN de type ODBC à la base de données locale, ainsi qu'une requête SQL de base que le Service utilisera pour interroger les tickets sur un minuteur défini dans le paramètre d'intervalle d'appel CallInterval.   Cette requête est associée au paramètre Key Field Name qui définit la clé unique de la requête et empêche le renvoi d'enregistrements déjà envoyés

Avec ceci en place, nous avons maintenant une production complète qui va scanner les nouveaux tickets, passer le texte à un service externe pour analyser la langue, et éventuellement réinitialiser le conseiller en fonction de la réponse.  Essayons cela!  Nous commençons par envoyer une requête en anglais, qui nous est retournée sous la forme TicketId 70.  Nous attendons une seconde, et accédons à cet enregistrement via le service GetTicket REST, ici le conseiller est inchangé par rapport à la requête originale

Répétons l'opération avec le texte en français (French Text)

Lorsque nous réclamons le ticket 71, notre conseiller a été changé en Mme Francis, comme nous nous y attendions! Nous pouvons le vérifier dans l'Interoperability en localisant le message dans Visual Trace, et en vérifiant que les Opérations ont été appelées comme prévu.

Nous en sommes maintenant à 142 lignes de code écrites, et nous avons une application d'InterSystems IRIS qui persiste les données, les a chargées en utilisant LOAD DATA, fournit une API REST de base pour la visualisation et l'édition des données, et un moteur d'intégration avancé fournissant un Support de décision basé sur des appels à un service REST externe.  Assurément, personne ne peut demander mieux?

Étape 5 – En savoir plus: Analyse

Votre application est un succès retentissant et les données affluent.   L'accès à ces données précieuses nécessite une certaine expertise, et la direction de Widgets Direct souhaite obtenir des informations.  Est-il possible de fournir un accès interactif aux données?

Grâce à InterSystems IRIS Analytics, nous pouvons fournir un accès facile et rapide à des outils de manipulation de données évolués.   Nous devons d'abord activer le support Analytics sur notre application web interne: 

Cela nous permet d'utiliser la section Analytics par rapport à notre Espace de noms.  Commencez par ouvrir Analytics>Architect.  Sélectionnez Nouveau (New) et remplissez le formulaire pour créer un Cube d'analyse pour la classe de tickets. 

Ensuite, nous pouvons configurer nos dimensions et une liste déroulante Drilldown à l'aide du constructeur Visual Builder.   Cette vue est accessible par glisser-déposer.  Une liste peut également être créée à l'aide d'un constructeur visuel simple, afin de personnaliser ce que l'utilisateur voit lorsqu'il interroge un point de données

Une fois la configuration de base établie, nous pouvons Sauvegarder, Compiler et Construire le Cube.  Cela permettra de configurer tous les indices et d'activer le Cube pour l'analyse dans le logiciel Analyzer.   Ouvrez l'Analyzer pour commencer à jouer avec les données.   Dans l'exemple présenté, nous pouvons facilement comparer les conseillers par rapport à une hiérarchie d'années et de trimestres filtrés par le contact en question.   Une fois que vous avez cliqué sur une cellule, vous pouvez appuyer sur l'icône des jumelles pour appeler la liste Drilldown que vous avez créée en vue d'une analyse et d'une exportation plus approfondies

Conclusion

Avec seulement 142 lignes de code, nous disposons d'une application BackEnd moderne, simple mais fonctionnelle, avec des outils permettant la communication inter-applications et l'analyse avancée.   Il s'agit d'une mise en œuvre simplifiée à l'extrême qui ne doit être utilisée que comme exemple des éléments de base nécessaires à la construction d'une application de base de données dans IRIS.  Comme il a été mentionné au début de l'article, ce code n'est pas prêt pour la production et, dans le cadre d'une utilisation réelle, les développeurs doivent se référer à la documentation et aux meilleures pratiques d'InterSystems IRIS pour s'assurer que leur code est robuste, sécurisé et évolutif (aucune de ces caractéristiques ne s'applique à cette base de code).  Bon codage!

1
0 99
Article Sylvain Guilbaud · Fév 23, 2024 5m read

Dans cet article, je partagerai le thème que nous avons présenté lors du Global Summit 2023, dans la salle Tech Exchange. Moi et @Rochael Ribeiro

Lors de cette présentation, nous abordons les sujets suivants :

  • Outils Open Exchange pour des API rapides
  • Spécification de l'Open API
  • Développement d'API traditionnel ou Fast
  • API composite (interopérabilité)
  • Approche Spec-First ou Api-First
  • Gouvernance et surveillance des API
  • Démo (vidéo)

Outils Exchange ouverts pour des API Fast

0
2 113
Article Pierre LaFay · Jan 13, 2024 3m read

Bienvenue à tous!

Dans ce court article, je voudrais présenter un exemple d'utilisation auquel beaucoup d'entre vous qui travaillent avec IRIS comme backend pour vos applications Web ont sûrement été confrontés à plus d'une occasion : comment envoyer un fichier à votre serveur depuis le frontend. .

0
0 54
Article Pierre LaFay · Jan 6, 2024 23m read

Jusqu'à présent, nous avons expliqué comment utiliser ObjectScript pour gérer les utilisateurs, les rôles, les ressources et les applications. Il existe quelques autres classes dans ce package qui fonctionnent de manière similaire à celles mentionnées ci-dessus. Cependant, ces quatre classes sont celles que chacun devra utiliser pour gérer la sécurité de ses applications. Supposons que vous souhaitiez créer votre propre portail de gestion de la sécurité pour ce package. Il y aurait des problèmes spécifiques à prendre en compte pour une API. Étant donné que les classes utilisent des méthodes

1
0 64
Article Niels Genne · Nov 24, 2023 4m read

Comment déployer les productions IRIS sereinement et plus rapidement ?

L'objectif des productions d'interopérabilité est de vous permettre de connecter des systèmes afin de transformer et d'acheminer des messages entre eux. Pour connecter des systèmes, vous développez, configurez, déployez et gérez des productions qui intègrent plusieurs solutions.

C’est ce que nous dit la documentation InterSystems sur son site de référence, mais que faut-il réellement faire pour déployer une production ?

On peut composer les productions, selon les usages, pour connecter des systèmes externes à IRIS Data Platform. Il est pour cela nécessaire de créer un environnement propre à chaque production à savoir les composants suivants :

  • un Business service 📨
  • un Business processus (facultatif) 📘
  • une Business opération 💉
  • des schémas de définition de table (.cls; classes) 📅
  • un fichier d’initialisation d’espace de noms (.cpf) 📋

Bien sûr l’importance d’employer les productions pour traiter les messages réside dans le fait de pouvoir tracer chaque message et remonter ainsi la trace des accidents de parcours de chaque événement indésirable.

Et si je vous disais qu’il est possible de déployer vos productions à l’aide de notre framework d’interopérabilité IRIS d’un coup de baguette magique ?🪄

Explications

L’approche mainframe de laquelle provient notre framework permet de déployer à vitesse grand « V » des productions IRIS InterSystems® sans devoir recréer tous les composants à la main.

L’emploi du framework permet d’ajouter une fonctionnalité intéressante autorisant la lecture des données des tables à déployer avec la production : l’ajout d’une API sortante (RestForms2).

Sounds good :)

➡️Les données deviennent ainsi interrogeables et restituables dans un format JSON.

Le framework va générer tous les composants en se basant sur un fichier de spécifications fonctionnelles rempli en accord avec le métier et notre chef de projet (dont le rôle est de veiller à ce que chaque information nécessaire trouve sa place).

Le script agit en deux temps à savoir : la construction du flux ETL et du point de chute des données. 📦🪂

Une fois rempli conformément à ce qui est attendu, le fichier de spécifications fonctionnelles permet de générer dans un premier temps : le fichier de sérialisation des messages (classes de données ; obj.py), le fichier de structure des données dans chaque message (msg.py), le fichier de génération de messages (bs.py) et le fichier d’ingestion des messages dans la base de données correspondante (bo.py); dans un second temps : il sert à créer/supprimer les tables dans la base de données sous forme d’un script SQL comprenant des instructions DDL (Data Definition Language).

De quoi vous faire gagner beaucoup de temps ! ⌚ Le plus beau dans tout cela, c’est que le framework est déployable facilement depuis un container Docker ! 🐳

Intérêts

Toujours pas convaincu(e) ? En quoi utiliser ce framework vous ferait gagner 80% du temps ?⏱️

Et si je vous disais que le code déployé par le framework est validé par l’éditeur InterSystems®, qu’il permet à votre équipe de travailler sur du code uniformisé, que lors de campagnes de maintenance cette possibilité vous incite à être plus efficace en cas de mise à jour de code ou de recherche de bug, qu’il vous permet d’interagir avec vos données à l’aide d’un mécanisme de REST API (issu du dépôt des paquets compatibles InterSystems IRIS toutes versions confondues). Cela fait sens pour vous ? 👌

Qu’entend-on par « le code est validé par l’éditeur » ? ✅ Simplement qu’il respecte les standards Python et ceux de l’éditeur en matière d’architecture, d’appels aux mécanismes internes d’IRIS InterSystems® également qu’il sait s’interfacer avec le langage ObjectScript et vice versa.

La suite

Si cet article fait écho à vos besoins ou si vous êtes simplement curieux de voir comment ce framework pourrait révolutionner votre manière de travailler sous IRIS InterSystems® : rendez vous sur notre site web et/ou demander un accès à notre Discord Server pour discuter avec l’un de nos experts.

Suivez-nous aussi sur LinkedIn profile

Prochainement, nous vous ferons découvrir un cas d’usage du framework en environnement opérationnel 😉 !

0
0 105
Article Sylvain Guilbaud · Oct 2, 2023 13m read

Pour le prochain Concours Python, j'aimerais faire une petite démo, sur la création d'une simple application REST en Python, qui utilisera IRIS comme base de données. Et utiliser les outils suivants

  • Le cadre FastAPI, très performant, facile à apprendre, rapide à coder, prêt pour la production.
  • SQLAlchemy est la boîte à outils SQL et le Mapping objet-relationnel de Python qui donne aux développeurs en Python toute la puissance et la flexibilité de SQL.
  • Alembic est un outil léger de migration de base de données à utiliser avec le SQLAlchemy Database Toolkit pour Python.
  • Uvicorn est une implémentation de serveur web ASGI pour Python.

Préparation de l'environnement

En supposant que Python soit déjà installé, au moins en version 3.7., il faut créer un dossier de projet, et y créer un fichier requirements.txt avec le contenu suivant

fastapi==0.101.1
alembic==1.11.1
uvicorn==0.22.0
sqlalchemy==2.0.20
sqlalchemy-iris==0.10.5

 Je vous conseille d'utiliser l'environnement virtuel en python, nous allons créer un nouvel environnement et l'activer.

python -m venv env && source env/bin/activate

Et maintenant, nous pouvons installer nos dépendances

pip install -r requirements.txt

Démarrage rapide

Créons l'Api REST la plus simple avec FastAPI. Pour ce faire, créons app/main.py

from fastapi import FastAPI

app = FastAPI(
    title='TODO Application',
    version='1.0.0',
)

@app.get("/ping")asyncdefpong():return {"ping": "pong!"}

Pour l'instant, il suffit de démarrer notre application, et elle devrait déjà fonctionner. Pour démarrer le serveur, nous allons utiliser uvicorn

$ uvicorn app.main:app         
INFO:     Processus de serveur lancé [94936]
INFO:     En attente du lancement de l'application.
INFO:     Application startup compléte.
INFO:     Uvicorn fonctionne sur http://127.0.0.1:8000 ( Appuyez sur CTRL+C pour quitter)

Et nous pouvons soumettre une requête de ping.

$ curl http://localhost:8000/ping
{"ping":"pong!"}

FastAPI propose une interface utilisateur permettant de tester l'API.

Environnement Dockerisé

Pour ajouter IRIS à notre application, nous allons utiliser des conteneurs. L'image d'IRIS sera utilisée telle quelle, mais il nous faut construire une image Docker pour l'application python. Et nous aurons besoin de Dockerfile

FROM python:3.11-slim-buster

WORKDIR /usr/src/app
RUN --mount=type=bind,src=.,dst=. \
    pip install --upgrade pip && \
    pip install -r requirements.txt
COPY . .
ENTRYPOINT [ "/usr/src/app/entrypoint.sh" ]

Pour lancer l'application à l'intérieur du conteneur, il faut un simple entrypoint.sh.

#!/bin/sh
# Exécution des migrations SQL, pour mettre à jour le schéma de la base de données
alembic upgrade head

# Lancement de l'application Python
uvicorn app.main:app \
      --workers 1 \
      --host 0.0.0.0 \
      --port 8000 "$@"

N'oubliez pas d'ajouter un drapeau d'exécution

chmod +x entrypoint.sh

Et combinez avec IRIS dans docker-compose.yml

version:"3"services:  iris:    image:intersystemsdc/iris-community    ports:      -1972    environment:      -IRISUSERNAME=demo      -IRISPASSWORD=demo    healthcheck:      test:/irisHealth.sh      interval:5s  app:    build:.    ports:      -8000:8000    environment:      -DATABASE_URL=iris://demo:demo@iris:1972/USER    volumes:      -./:/usr/src/app    depends_on:      iris:        condition:service_healthy    command:      ---reload

Construisons-le

docker-compose build

Le premier modèle de données

Maintenant, déclarons l'accès à notre base de données IRIS à l'application, en ajoutant le fichier app/db.py, qui configurera SQLAlchemy pour accéder à notre base de données définie à travers l'URL passée par docker-compose.yml. Ce fichier contient également quelques gestionnaires que nous utiliserons plus tard dans l'application.

import os

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base
from sqlalchemy.orm import sessionmaker

DATABASE_URL = os.environ.get("DATABASE_URL")
ifnot DATABASE_URL:
    DATABASE_URL = "iris://demo:demo@localhost:1972/USER"
engine = create_engine(DATABASE_URL, echo=True, future=True)

Base: DeclarativeMeta = declarative_base()

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


definit_db():
    engine.connect()


defget_session():
    session = SessionLocal()
    yield session

Et nous sommes prêts à définir le premier et unique modèle de notre application. Nous créons et éditons le fichier app/models.py, il utilisera SQLAlchemy pour définir le modèle, nommé Todo, à trois colonnes, id, title, et description.

from sqlalchemy import Column, Integer, String, Text
from app.db import Base


classTodo(Base):
    __tablename__ = 'todo'
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String(200), index=True, nullable=False)
    description = Column(Text, nullable=False)

Préparation de la migration SQL

Dans ce monde changeant, nous savons que notre application sera améliorée à l'avenir, que la structure de nos tableaux n'est pas définitive, que nous pouvons ajouter des tableaux, des colonnes, des index, etc. Dans ce cas, le meilleur scénario consiste à utiliser des outils de migration SQL, qui permettent de mettre à jour la structure actuelle de la base de données en fonction de la version de notre application, et grâce à ces outils, il est également possible de la rétrograder, au cas où quelque chose ne fonctionnerait pas. Bien que dans ce projet nous utilisions Python et SQLAlchemy, l'auteur de SQLAlchemy propose son outil nommé Alembic, et nous allons l'utiliser ici.

We need to start IRIS and container with our application, at this moment we need bash, to be able to run commands

$ docker-compose run --entrypoint bash app
[+] Creating 2/0
 ✔ Réseau  fastapi-iris-demo_default   Crée                                                                                                                                                        0.0s 
 ✔ Conteneur fastapi-iris-demo-iris-1  Crée                                                                                                                                                        0.0s 
[+] Exécution 1/1
 ✔ Conteneur fastapi-iris-demo-iris-1  Lancé                                                                                                                                                        0.1s 
root@7bf903cd2721:/usr/src/app# 

Exécution de la commande alembic init app/migrations

root@7bf903cd2721:/usr/src/app# alembic init app/migrations
  Création du répertoire '/usr/src/app/app/migrations' ...  exécuté
  Création du répertoire '/usr/src/app/app/migrations/versions' ...  exécuté
  Génération de /usr/src/app/app/migrations/README ...  exécuté
  Génération de /usr/src/app/app/migrations/script.py.mako ...  exécuté
  Génération de /usr/src/app/app/migrations/env.py ...  exécuté
  Génération de /usr/src/app/alembic.ini ...  exécuté
  Veuillez modifier les paramètres de configuration/connexion/logging dans '/usr/src/app/alembic.ini' avant de continuer.
root@7bf903cd2721:/usr/src/app#

Cette configuration alembic a été préalablement configurée, et nous devons la corriger pour qu'elle corresponde aux besoins de notre application. Pour ce faire, il faut éditer le fichier app/migrations/env.py. Ce n'est que le début du fichier, qui doit être mis à jour, en se concentrant sur la mise à jour de sqlalchemy.url et target_metadata. Ce qui se trouve en dessous reste inchangé

import os
import urllib.parse
from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context

# il s'agit de l'objet Alembic Config, qui permet# d'accéder aux valeurs du fichier .ini utilisé.
config = context.config

DATABASE_URL = os.environ.get("DATABASE_URL")

decoded_uri = urllib.parse.unquote(DATABASE_URL)
config.set_main_option("sqlalchemy.url", decoded_uri)

# Interprétation du fichier de configuration pour l'enregistrement Python.# Cette ligne met en place les enregistreurs de façon basique.if config.config_file_name isnotNone:
    fileConfig(config.config_file_name)

# ajoutez ici l'objet MetaData de votre modèle# pour la prise en charge de l'autogénérationfrom app.models import Base
target_metadata = Base.metadata
# target_metadata = non applicable

Nous avons déjà un modèle, maintenant il faut créer une migration, avec la commande alembic revision --autogenerate (alembic revision ---autogénérer).

root@7bf903cd2721:/usr/src/app# alembic revision --autogenerate
INFO  [alembic.runtime.migration] Contexte impl IRISImpl.
INFO  [alembic.runtime.migration] Cela suppose un DDL non transactionnel.
INFO  [alembic.autogenerate.compare] Détection du tableau "todo" ajouté
INFO  [alembic.autogenerate.compare] Détection d'un index ajouté 'ix_todo_id' sur '['id']'
INFO  [alembic.autogenerate.compare] Détection d'un index ajouté 'ix_todo_title' sur '['title']'
  Generating /usr/src/app/app/migrations/versions/1e4d3b4d51ca_.py ... exécuté
root@7bf903cd2721:/usr/src/app# 
 
Let's see generated migration

Maintenant il faut appliquer ceci à la base de données, avec la commande alembic upgrade head, où "head" est un mot-clé pour mettre à jour vers la dernière version.

root@7bf903cd2721:/usr/src/app# alembic upgrade head
INFO  [alembic.runtime.migration] Contexte impl IRISImpl.
INFO  [alembic.runtime.migration] Cela suppose un DDL non transactionnel.
INFO  [alembic.runtime.migration] Exécution de la mise à jour -> 1e4d3b4d51ca, message vide
 
Rétrogradation
Si, au cours de la mise à jour de l'application, nous découvrons que nous devons revenir en arrière, nous pouvons rétrograder la base de données, par exemple la dernière révision sera head-1.
<pre>root@7bf903cd2721:/usr/src/app# alembic downgrade head-1

INFO  [alembic.runtime.migration] Contexte impl IRISImpl. INFO  [alembic.runtime.migration] Cela suppose un DDL non transactionnel. INFO  [alembic.runtime.migration] Exécution de la rétrogradation 1e4d3b4d51ca -> , message vide

<p>
  et pour rétrograder complètement vers un état vide, utilisez le mot-clé <strong>base</strong>
</p>

Vérifiez l'état actuel à tout moment, ce qui vous donnera des informations sur les migrations manquantes.

root@7bf903cd2721:/usr/src/app# alembic check
INFO  [alembic.runtime.migration] Contexte impl IRISImpl.
INFO  [alembic.runtime.migration] Cela suppose un DDL non transactionnel.
Aucune nouvelle opération de mise à jour détectée.

Accessibilité des données

Donc, nous pouvons maintenant retourner au REST, et il nous faut le faire fonctionner, quitter le conteneur actuel et lancer le service d'application comme d'habitude maintenant, uvicorn a un drapeau --reload, donc, il vérifiera les changements dans les fichiers python et redémarrera lorsque nous les changerons.

$ docker-compose up app
[+] Running 2/0
 ✔ Conteneur fastapi-iris-demo-iris-1 Lancé                                                                                                                                                        0.0s 
 ✔ Conteneur fastapi-iris-demo-app-1   Crée                                                                                                                                                       0.0s 
Attaching to fastapi-iris-demo-app-1, fastapi-iris-demo-iris-1
fastapi-iris-demo-app-1   | INFO  [alembic.runtime.migration] Contexte impl IRISImpl.
fastapi-iris-demo-app-1   | INFO  [alembic.runtime.migration] Cela suppose un DDL non transactionnel.
fastapi-iris-demo-app-1   | INFO:     Surveillance des modifications apportées aux répertoires : ['/usr/src/app']
fastapi-iris-demo-app-1   | INFO:     Uvicorn lancé sur http://0.0.0.0:8000 (Appuyez sur CTRL+C pour quitter)
fastapi-iris-demo-app-1   | INFO:     Lancement du processus de rechargement [8] à l'aide de StatReload
fastapi-iris-demo-app-1   | INFO:     Lancement du processus de serveur [10]
fastapi-iris-demo-app-1   | INFO:     En attente du démarrage de l'application.
fastapi-iris-demo-app-1   | INFO:     Démarrage de l'application achevé.

FastAPI utilise le projet pydantic, pour déclarer le schéma de données, et nous en avons besoin aussi, créons app/schemas.py, les mêmes colonnes que dans models.py mais sous une forme simple en Python

from pydantic import BaseModel


classTodoCreate(BaseModel):
    title: str
    description: str


classTodo(TodoCreate):
    id: int

    classConfig:
        from_attributes = True

Déclaration des opérations crud dans app/crud.py, où nous travaillons avec la base de données en utilisant l'ORM de SQLAlchemy.

from sqlalchemy.orm import Session
from . import models, schemas


defget_todos(db: Session, skip: int = 0, limit: int = 100):return db.query(models.Todo).offset(skip).limit(limit).all()


defcreate_todo(db: Session, todo: schemas.TodoCreate):
    db_todo = models.Todo(**todo.dict())
    db.add(db_todo)
    db.commit()
    db.refresh(db_todo)
    return db_todo

Pour finir, nous pouvons mettre à jour notre app/main.py, et ajouter des itinéraires pour lire et créer des todos.

from fastapi import FastAPI, Depends
from .db import init_db, get_session
from . import crud, schemas

app = FastAPI(
    title='TODO Application',
    version='1.0.0',
)


@app.on_event("startup")defon_startup():
    init_db()


@app.get("/ping")asyncdefpong():return {"ping": "pong!"}


@app.get("/todo", response_model=list[schemas.Todo])asyncdefread_todos(skip: int = 0, limit: int = 100, session=Depends(get_session)):
    todos = crud.get_todos(session, skip=skip, limit=limit)
    return todos


@app.post("/todo", response_model=schemas.Todo)asyncdefcreate_todo(todo: schemas.TodoCreate, session=Depends(get_session)):return crud.create_todo(db=session, todo=todo)

La page de documentation "docs" a été mise à jour en conséquence, et nous pouvons maintenant jouer avec.

 
Essayez !
Ajouter une nouvelle todo
<p>
  <img src="/sites/default/files/inline/images/images/image(6813).png" />
</p>

<p>
  Et vérifiez ce que nous avons ici
</p>

<p>
  <img src="/sites/default/files/inline/images/images/image(6814).png" />
</p>

Vérifions-le dans IRIS

─$ docker-compose exec iris irissqlcli iris+emb:///
Serveur:  IRIS pour UNIX (Ubuntu Server LTS pour les conteneurs "ARM64 Containers") 2023.2 (Build 227U) Mon Jul 31 2023 17:43:25 EDT
Version: 0.5.4
[SQL]irisowner@/usr/irissys/:USER> .tables
+-------------------------+
| TABLE_NAME              |
+-------------------------+
| SQLUser.alembic_version |
| SQLUser.todo            |
+-------------------------+
Temps: 0.043s
[SQL]irisowner@/usr/irissys/:USER> select * from todo
+----+-------+---------------------+
| id | titre | description         |
+----+-------+---------------------+
| 1  | démo  | cela marche vraiment |
+----+-------+---------------------+
1 rang dans le jeu
Temps: 0.004s
[SQL]irisowner@/usr/irissys/:USER> select * from alembic_version
+--------------+
| version_num  |
+--------------+
| 1e4d3b4d51ca |
+--------------+
1 rang dans le jeu
Temps: 0.045s
[SQL]irisowner@/usr/irissys/:USER>

 

J'espère que vous avez apprécié la facilité d'utilisation de Python et de FastAPI pour la création de REST. Le code source de ce projet est disponible sur github https://github.com/caretdev/fastapi-iris-demo

0
0 126
Article Iryna Mykhailova · Sept 25, 2023 6m read

FHIR a transformé le secteur des soins de santé en fournissant un modèle de données normalisé pour la création d'applications de soins de santé et en favorisant l'échange de données entre les différents systèmes de soins de santé. La norme FHIR est basée sur des approches modernes axées sur les API, ce qui la rend plus accessible aux développeurs mobiles et web. Cependant, l'interaction avec les API FHIR peut encore s'avérer difficile, en particulier lorsqu'il s'agit de requêter des données à l'aide du langage naturel.

Nous présentons l'application Chaîne OpenAPI et IA - FHIR une solution qui permet aux utilisateurs d'interagir avec les API FHIR à l'aide de requêtes en langage naturel. Conçue avec OpenAI, LangChain et Streamlit, cette application simplifie le processus d'interrogation des API FHIR et le rend plus facile à utiliser.

 

Quelles sont les spécifications OpenAPI de FHIR ?

Les spécifications OpenAPI (anciennement connues sous le nom de "Swagger" et faisant actuellement partie de l'" Initiative OpenAPI " (https://www.openapis.org/)) sont devenues un outil essentiel dans le monde du développement logiciel, permettant aux développeurs de concevoir, de documenter et d'interagir avec les API de manière plus efficace. Les spécifications OpenAPI définissent un format normalisé, lisible par une machine, pour décrire les API RESTful, offrant ainsi un moyen clair et cohérent de comprendre leurs capacités et de les utiliser de manière efficace.

Dans le domaine des soins de santé, FHIR s'est imposé comme une norme de premier plan pour l'échange de données et l'interopérabilité. Pour améliorer les capacités d'interopérabilité de FHIR, HL7 a officiellement documenté les spécifications OpenAPI de FHIR, qui permettent aux développeurs d'intégrer de manière transparente les ressources et les opérations FHIR dans leurs solutions logicielles.

 

Avantages des spécifications OpenAPI de FHIR :

  1. Description normalisée de l'API : Les spécifications OpenAPI fournissent une description complète et normalisée des ressources, des opérations et des interactions FHIR. Les développeurs peuvent facilement comprendre la structure et les capacités des API basées sur FHIR, ce qui facilite la création d'intégrations et l'interaction avec les systèmes de santé.
  2. Promotion de l'interopérabilité : La collaboration entre les développeurs est encouragée, ce qui favorise l'adoption des normes et des meilleures pratiques FHIR. Les spécifications fournissent un langage et un cadre communs pour discuter des intégrations et des implémentations basées sur FHIR, ce qui favorise la collaboration entre les développeurs.
  3. Documentation et tests améliorés : Documentation interactive et séries de tests pour une meilleure compréhension et une meilleure validation. Les développeurs peuvent créer une documentation détaillée sur les API, ce qui permet aux autres développeurs de comprendre et d'utiliser plus facilement les API basées sur FHIR. Les séries de tests basées sur les spécifications permettent d'effectuer des tests complets et de valider les intégrations d'API, garantissant ainsi la fiabilité et la précision des échanges de données de santé.
  4. Expérience améliorée pour les développeurs : Génération automatique de bibliothèques client et de SDK pour une intégration transparente. Cela simplifie le processus d'intégration et réduit le temps et les efforts nécessaires pour incorporer la fonctionnalité FHIR dans leurs applications.

 

Comment les chaînes FHIR, OpenAI et OpenAPI fonctionnent-elles ensemble ?

L'application Chaîne OpenAPI et IA - FHIR s'appuie sur LangChain pour charger et analyser les spécifications OpenAPI (Chaîne OpenAPI). Ensuite, sur la base de ces spécifications, la chaîne d'invites fournies par OpenAI vise à comprendre les requêtes en langage naturel et à les convertir en demandes d'API FHIR appropriées. Les utilisateurs peuvent poser des questions en langage naturel et l'application interagira avec l'API FHIR choisie pour récupérer les informations pertinentes.

Par exemple, un utilisateur peut demander : "Quelle est la dernière mesure de la tension artérielle du patient John Doe (ID 111) ?" L'application traduira alors cette requête en une demande d'API FHIR, récupérera les données requises et les présentera à l'utilisateur dans un format facilement compréhensible.

 

Avantages de l'application Chaîne OpenAPI et IA - FHIR

  1. Interactions faciles à utiliser : L'application permet aux utilisateurs d'interagir avec les API FHIR à l'aide de requêtes en langage naturel, ce qui facilite l'accès et l'analyse des données de santé pour les utilisateurs non techniques.
  2. Amélioration de l'efficacité : L'application rationalise le processus d'interrogation des API FHIR, réduisant ainsi le temps et les efforts nécessaires pour obtenir des informations pertinentes. Elle permet également de réduire le nombre de clics (temps passé) pour trouver une information particulière à partir de l'application.
  3. Personnalisable : Les normes FHIR simplifient la récupération de données cohérentes à partir de n'importe quel serveur FHIR, ce qui permet une personnalisation aisée. Il peut être configuré sans effort pour s'intégrer de manière transparente à n'importe quelle API FHIR, offrant ainsi une solution flexible et adaptable aux diverses exigences en matière de données de santé.

 

Premiers pas avec l'application Chaîne OpenAPI et IA - FHIR

Pour commencer à utiliser l'application Chaîne OpenAPI et IA - FHIR, suivez les étapes suivantes :

  1. Obtenez une clé API d'OpenAI auprès de la Plate-forme OpenAI.
  2. Obtenez un point de terminaison API du serveur FHIR. Vous pouvez utiliser votre propre exemple de serveur FHIR (accès non authentifié nécessaire) ou créer un exemple de serveur temporaire en suivant les instructions données dans la Plate-forme d'apprentissage de FHIR pour InterSystems IRIS.
  3. Essayez l'application en ligne ou ou configurez-la localement en suivant les instructions fournies.

Grâce à l'intégration de capacités d'IA et de traitement du langage naturel, l'application haîne OpenAPI et IA - FHIR offre un moyen plus intuitif pour interagir avec les API FHIR, rendant les données de santé plus accessibles et plus faciles à analyser pour les utilisateurs de tous niveaux techniques.

Votez pour notre application dans le cadre du concours Grand Prix si vous la trouvez pertinente !

Si vous pensez à des applications qui pourraient utiliser cette implémentation, n'hésitez pas à les partager dans le fil de discussion.

0
0 73
Question Julia Pertin · Sept 18, 2023

Bonjour à tous, 

Pour un interop je dois récupérer en entrée un json d'une API REST. J'essaie donc de passer le %Net.HttpRequest or j'obtiens cette erreur : 

J'ai mis cette classe en entrée du business process et j'ai un business service qui appel à intervalle régulier ce business process : 

Pouvez-vous m'aider s'il vous plait ? 

1
1 82
Article Sylvain Guilbaud · Juil 17, 2023 11m read

Le besoin de créer des services REST permettant d'accéder aux informations présentes dans IRIS / HealthConnect est l'un des besoins les plus courants de nos clients. L'avantage de ces services REST est la possibilité de développer des interfaces utilisateurs personnalisées avec les technologies les plus récentes en profitant de la fiabilité et de la performance d'IRIS dans le back-end.

Dans l'article d'aujourd'hui, nous allons créer pas à pas un service web qui nous permettra à la fois de stocker des données dans notre base de données et de les consulter ultérieurement. De plus, nous allons le faire en configurant une production qui nous permettra de contrôler le flux de messages que nous recevons à tout moment et de contrôler ainsi son bon fonctionnement.

Avant de commencer, sachez simplement que vous avez tout le code disponible sur Open Exchange pour que vous puissiez le répliquer autant de fois que nécessaire, et que le projet est configuré dans Docker, en utilisant la communauté IRIS afin que vous n'ayez rien d'autre à faire que de le déployer.

Bien! Commençons donc!

Préparation de l'environnement

Avant de commencer la configuration du service REST, nous devons préparer notre environnement de développement, savoir quelles informations nous allons recevoir et ce que nous allons en faire. Pour cet exemple, nous avons décidé de recevoir des données de la personne dans le format JSON suivant :

{
    "PersonId": 1,
    "Name": "Irene",
    "LastName": "Dukas",
    "Sex": "Female",
    "Dob": "01/04/1975"
}

Comme l'un des objectifs est de stocker les informations que nous recevons, nous allons créer une classe Objectscript qui nous permettra d'enregistrer les informations dans notre IRIS. Comme vous pouvez le constater, les données sont assez simples, donc la classe n'aura pas beaucoup de complications :

Class WSTEST.Object.Person Extends%Persistent
{

/// ID of the personProperty PersonId As%Integer;/// Name of the personProperty Name As%String;/// Lastname of the personProperty LastName As%String;/// Sex of the personProperty Sex As%String;/// DOB of the personProperty Dob As%String;
Index PersonIDX On PersonId [ PrimaryKey ];
}

Parfait, nous avons déjà défini notre classe et nous pouvons commencer à nous en servir.

Création de notre point de terminaison

Maintenant que nous avons défini la classe de données avec laquelle nous allons travailler, il est temps de créer notre classe Objectscript qui fonctionnera comme un point de terminaison qui sera appelé depuis notre front-end. Voyons étape par étape la classe d'exemple que nous avons dans notre projet :

Class WSTEST.Endpoint Extends%CSP.REST
{

Parameter HandleCorsRequest = 0; XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ] { <Routes> <Route Url="/testGet/:pid" Method="GET" Call="TestGet" /> <Route Url="/testPost" Method="POST" Call="TestPost" /> </Routes> }

ClassMethod OnHandleCorsRequest(url As%String) As%Status { set url = %request.GetCgiEnv("HTTP_REFERER") set origin = $p(url,"/",1,3) // origin = "http(s)://origin.com:port"// here you can check specific origins// otherway, it will allow all origins (useful while developing only)do%response.SetHeader("Access-Control-Allow-Credentials","true") do%response.SetHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS") do%response.SetHeader("Access-Control-Allow-Origin",origin) do%response.SetHeader("Access-Control-Allow-Headers","Access-Control-Allow-Origin, Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control") quit$$$OK } // Class method to retrieve the data of a person filtered by PersonIdClassMethod TestGet(pid As%Integer) 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// Creation of BS instanceset status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSearchBS", .instance)

    <span class="hljs-comment">// Invocation of BS with pid parameter</span>
    <span class="hljs-keyword">set</span> status = instance.OnProcessInput(pid, .response)
   	<span class="hljs-keyword">if</span> <span class="hljs-built_in">$ISOBJECT</span>(response) {
        <span class="hljs-comment">// Sending person data to client in JSON format</span>
    	<span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>(response.<span class="hljs-built_in">%JSONExport</span>())
	}
    
} <span class="hljs-keyword">Catch</span> (ex) {
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%SetStatusCode</span>(<span class="hljs-string">"400"</span>)
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>(ex.DisplayString())
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"errormessage"</span>: <span class="hljs-string">"Client error"</span>}
}
<span class="hljs-keyword">Quit</span> <span class="hljs-built_in">$$$OK</span>

} // Class method to receive person data to persist in our databaseClassMethod TestPost() 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// Reading the body of the http call with the person dataset bodyJson = %request.Content.Read()

    <span class="hljs-comment">// Creation of BS instance</span>
    <span class="hljs-keyword">set</span> status = <span class="hljs-keyword">##class</span>(Ens.Director).CreateBusinessService(<span class="hljs-string">"WSTEST.BS.PersonSaveBS"</span>, .instance)
   	<span class="hljs-keyword">#dim</span> response <span class="hljs-keyword">as</span> <span class="hljs-built_in">%DynamicObject</span>
    <span class="hljs-comment">// Invocation of BS with person data</span>
    <span class="hljs-keyword">set</span> status = instance.OnProcessInput(bodyJson, .response)
    
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">$ISOBJECT</span>(response) {
        <span class="hljs-comment">// Returning to the client the person object in JSON format after save it</span>
        <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>(response.<span class="hljs-built_in">%JSONExport</span>())
    }
    
} <span class="hljs-keyword">Catch</span> (ex) {
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%SetStatusCode</span>(<span class="hljs-string">"400"</span>)
    <span class="hljs-keyword">Do</span> <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%REST.Impl</span>).<span class="hljs-built_in">%WriteResponse</span>(ex.DisplayString())
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"errormessage"</span>: <span class="hljs-string">"Client error"</span>}
}
<span class="hljs-keyword">Quit</span> <span class="hljs-built_in">$$$OK</span>

}

}

Ne vous inquiétez pas si cela vous semble impossible à comprendre, voyons les parties les plus pertinentes de notre cours :

Déclaration de classe :

Class WSTEST.Endpoint Extends%CSP.REST

Comme vous pouvez le voir, notre classe WSTEST.Endpoint élargit %CSP.REST, ce qui est nécessaire pour pouvoir utiliser la classe en tant que point de terminaison.

Définition des chemins :

XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ]
{
<Routes>
	<Route Url="/testGet/:pid" Method="GET" Call="TestGet" />
	<Route Url="/testPost" Method="POST" Call="TestPost" />
</Routes>
}

Dans cet extrait de code, nous déclarons les chemins qui peuvent être appelés à partir de notre front-end.

Comme vous voyez, nous avons deux chemins déclarés, le premier sera un appel GET dans lequel nous recevrons le paramètre pid que nous utiliserons pour rechercher des personnes par leur identifiant et qui sera géré par la méthode de classe TestGet. Le second appel sera de type POST, dans lequel nous recevrons les informations sur la personne que nous devons enregistrer dans notre base de données et qui seront traitées par la méthode de classe TestPost.

Examinons les deux méthodes :

Récupération des données de la personne :

ClassMethod TestGet(pid As%Integer) 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// Creation of BS instanceset status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSearchBS", .instance)

        // Invocation of BS with pid parameterset status = instance.OnProcessInput(pid, .response)
       	if$ISOBJECT(response) {
            // Sending person data to client in JSON formatDo##class(%REST.Impl).%WriteResponse(response.%JSONExport())
		}
        
    } Catch (ex) {
        Do##class(%REST.Impl).%SetStatusCode("400")
        Do##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit$$$OK
}

Dans cette méthode, vous pouvez voir comment nous avons déclaré dans notre méthode de classe la réception de l'attribut pid que nous utiliserons dans la recherche ultérieure. Bien que nous aurions pu effectuer la recherche directement à partir de cette classe, nous avons décidé de le faire à l'intérieur d'une production pour pouvoir contrôler chacune des opérations, c'est pourquoi nous créons une instance du service métier Business Service WSTEST.BS.PersonSearchBS à laquelle nous appelons plus tard sa méthode OnProcessInput avec le pid reçu. La réponse que nous recevrons sera de type WSTEST.Object.PersonSearchResponse que nous transformerons en JSON avant de l'envoyer au requérant.

Conservation des données de la personne :

ClassMethod TestPost() 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// Reading the body of the http call with the person dataset bodyJson = %request.Content.Read()
        
        // Creation of BS instanceset status = ##class(Ens.Director).CreateBusinessService("WSTEST.BS.PersonSaveBS", .instance)
       	#dim response as%DynamicObject// Invocation of BS with person dataset status = instance.OnProcessInput(bodyJson, .response)
        
        if$ISOBJECT(response) {
            // Returning to the client the person object in JSON format after save itDo##class(%REST.Impl).%WriteResponse(response.%JSONExport())
	    }
        
    } Catch (ex) {
        Do##class(%REST.Impl).%SetStatusCode("400")
        Do##class(%REST.Impl).%WriteResponse(ex.DisplayString())
        return {"errormessage": "Client error"}
    }
    Quit$$$OK
}

Comme dans le cas précédent, nous aurions pu sauvegarder notre objet personne directement à partir de cette classe, mais nous avons décidé de le faire à partir d'une opération métier "Business Operation" qui sera appelée à partir du service métier "Business Service" WSTEST.BS.PersonSaveBS.

Comme vous pouvez le voir dans le code, nous récupérons les informations envoyées par le client dans l'appel POST en lisant le flux figurant dans %request.Content. La chaîne obtenue sera ce que nous transmettrons au Business Service..

Publication de notre point de terminaison

Pour ne pas allonger cet article, nous allons ignorer les explications relatives à la production, vous pouvez consulter le code directement dans le projet OpenExchange. La production que nous avons configurée est la suivante :

Nous avons deux services commerciaux déclarés, l'un pour recevoir les demandes de recherche et l'autre pour les demandes de stockage des données de la personne. Chacun d'entre eux invoquera une opération métier Business Operation appropriée.

Très bien, révisons ce que nous avons configuré dans notre projet :

  • 1 classe de point de terminaison qui recevra les requêtes des clients (WSTest.Endpoint).
  • 2 services métier "Business Services" qui seront appelés à partir de notre classe de point de terminaison (**WSTest.BS.PersonSaveBS **et WSTest.BS.PersonSearchBS).
  • 2 opérations métier "Business Operations" chargées d'effectuer la recherche et l'enregistrement des données (**WSTest.BS.PersonSaveBS **et WSTest.BS.PersonSearchBS).
  • 4 classes pour envoyer et recevoir des données au sein de la production qui s'étendent de Ens.Request et Ens.Response (WSTest.Object.PersonSearchRequestWSTest.Object.PersonSaveRequest, **WSTest.Object.PersonSearchResponse **y WSTest.Object.PersonSaveResponse).

Il ne nous reste plus qu'une dernière étape pour mettre en service notre service web, c'est sa publication. Pour ce faire, nous allons accéder à l'option du portail de gestion Administration du système --> Sécurité -> Applications -> Applications Web

Nous verrons une liste de toutes les applications Web configurées dans notre instance :

Créons notre application web :

Examinons les points à configurer :

  • Nom : Nous allons définir la route à utiliser pour faire les invocations à notre service, pour notre exemple ce sera /csp/rest/wstest
  • Espace de noms : l'espace de noms sur lequel le service web travaillera, dans ce cas nous avons créé WSTEST dans lequel nous avons configuré notre production et nos classes.
  • Activer l'application : activer le service web pour qu'il puisse recevoir des appels.
  • Activer - REST : En sélectionnant REST, nous indiquons que ce service web est configuré pour recevoir des appels REST. En sélectionnant REST, nous devons définir la classe de distribution qui sera notre classe de terminaison WSTEST.Endpoint.
  • Méthodes d'authentification autorisées : la configuration de l'authentification de l'utilisateur qui fait l'appel au service web. Dans ce cas, nous avons défini que l'authentification se fait par mot de passe, donc dans notre Postman nous configurerons le mode d'autorisation de base dans lequel nous indiquerons le nom d'utilisateur et le mot de passe. Nous pouvons définir l'utilisation de l'authentification JWT qui est très utile car elle n'expose pas les données de l'utilisateur et le mot de passe dans les appels REST, si vous êtes intéressé à plonger dans JWT, vous pouvez consulter [cet article] (https://community.intersystems.com/post/creating-rest-api-jwt-authentication-objectscript).

Une fois que nous avons fini de configurer notre application web, nous pouvons lancer quelques tests au moyen de Postman et en important le fichier WSTest.postman_collection.json présent dans le projet.

Test de notre point de terminaison

Une fois que tout est configuré dans notre projet et que la production a démarré, nous pouvons lancer quelques tests sur notre service web. Nous avons configuré superuser en tant qu'utilisateur requérant, de sorte que nous n'aurons pas de problèmes pour sauvegarder et récupérer des données. Dans le cas de l'utilisation d'un autre utilisateur, nous devons nous assurer qu'il a les rôles nécessaires assignés ou que nous les assignons dans l'onglet Rôles de l'application de la définition de notre application web.

Commençons par enregistrer une personne dans notre base de données :

Nous avons obtenu un 200, il semble donc que tout se soit bien passé, vérifions le message en production :

Tout s'est bien passé, le message avec le JSON a été correctement reçu dans BS et a été enregistré avec succès dans BO.

Essayons maintenant de récupérer les données de notre cher Alexios Kommenos :

Bingo ! Voici notre Alexios, avec toutes les informations le concernant. Vérifions le message en production :

Très bien, tout fonctionne comme il faut. Comme vous l'avez vu, il est très facile de créer un service web REST dans IRIS, vous pouvez utiliser ce projet comme base pour vos futurs développements et si vous avez des questions, n'hésitez pas à laisser un commentaire.

0
0 176
Article Lorenzo Scalese · Mars 13, 2023 4m read

Ce Template s'agit d'un modèle de l' application API REST construite avec ObjectScript dans Intersystems IRIS. Il dispose également d'une spécification OPEN API, peut être développé avec Docker et VSCode et peut être déployé en tant que module IPM.

Conditions préalables

Assurez-vous que vous avez installé git et Docker desktop.

Installation avec IPM

zpm:USER>install rest-api-template

Installation pour le développement

Créez votre référentiel à partir d'un modèle.

0
0 145
Article Lorenzo Scalese · Mars 3, 2023 9m read

Bonjour à la communauté,

Dans la première partie, nous avons décrit tous les packages, les bibliothèques utilisées et les services REST.  J'aimerais maintenant détailler un peu plus les services convertisseur et validateur.  Par défaut, OpenAPI-Suite envoie une requête HTTP  converter.swagger.io si a spécification est de version inférieure à 3.0 et une autre requête HTTP à validator.swagger.io pour simplifier la structure de la spécification.  

Bien que l'utilisation d'utilitaires en ligne soit pratique, dans certains cas, nous pourrions préférer avoir notre propre instance du convertisseur et du validateur.  Par exemple, si OpenAPI-Suite est mis à disposition sur un serveur dans une organisation pour les développeurs ObjectScript, il peut être préférable d'éviter les requêtes vers les services externes (confidentialité, éviter les limites de taux de demande,...).  Ceux-ci sont disponibles en images Docker, il suffit d'exécuter : 

docker run -d -p 8085:8080 --name swagger-converter swaggerapi/swagger-converter:latest
docker run -d -p 8086:8080 --name swagger-validator-v2 swaggerapi/swagger-validator-v2:latest
0
0 60
Article Lorenzo Scalese · Fév 27, 2023 18m read

Salut la communauté,

J'aimerais vous présenter ma dernière application OpenAPI-Suite, c'est un ensemble d'outils permettant de générer du code ObjectScript à partir d'une specification OpenAPI version 3.0. L'application permet de:

  • Générer les classes serveur REST.  C'est assez similaire au code généré par ^%RESTla valeur ajoutée est le support de la version 3.0.
  • Générer les classes pour un client HTTP.
  • Générer une production cliente (business services, business operation, business process, Ens.Request, Ens.Response).
  • Disposer d'une interface web pour générer et télécharger le code ou générer et compiler directement sur le serveur.
  • Convertir les spécifications de version 1.x, 2.x en version 3.0.

Aperçu

OpenAPI-Suite est divisée en plusieurs packages et utilise différentes bibliothèques de la communauté des développeurs ainsi que des services REST publics.  Vous pouvez voir sur le schéma ci-dessous, tous les packages développés et les bibliothèques et services web utilisés:
 

0
0 202
Article Guillaume Rongier · Jan 20, 2023 12m read

Intersystems IRIS for Health offre un excellent support pour la norme sectorielle FHIR. Les principales caractéristiques sont :

  1. Serveur FHIR
  2. Base de données FHIR
  3. API REST et ObjectScript pour les opérations CRUD sur les ressources FHIR (patient, questionnaire, vaccins, etc.)

Cet article explique comment utiliser chacune de ces fonctionnalités, et présente un front-end angulaire permettant de créer et d'afficher des ressources FHIR de type Quiz.

Étape 1 - Déploiement de votre serveur FHIR à l'aide d'InterSystems IRIS for Health

Pour créer votre serveur FHIR, il faut ajouter les instructions suivantes dans le fichier iris.script ( à partir de : https://openexchange.intersystems.com/package/iris-fhir-template)

    zn "HSLIB"
    set namespace="FHIRSERVER"
    Set appKey = "/fhir/r4"
    Set strategyClass = "HS.FHIRServer.Storage.Json.InteractionsStrategy"
    set metadataPackages = $lb("hl7.fhir.r4.core@4.0.1")
    set importdir="/opt/irisapp/src"

    //Install a Foundation namespace and change to it
    Do ##class(HS.HC.Util.Installer).InstallFoundation(namespace)
    zn namespace

    // Install elements that are required for a FHIR-enabled namespace
    Do ##class(HS.FHIRServer.Installer).InstallNamespace()

    // Install an instance of a FHIR Service into the current namespace
    Do ##class(HS.FHIRServer.Installer).InstallInstance(appKey, strategyClass, metadataPackages)

    // Configure FHIR Service instance to accept unauthenticated requests
    set strategy = ##class(HS.FHIRServer.API.InteractionsStrategy).GetStrategyForEndpoint(appKey)
    set config = strategy.GetServiceConfigData()
    set config.DebugMode = 4
    do strategy.SaveServiceConfigData(config)

    zw ##class(HS.FHIRServer.Tools.DataLoader).SubmitResourceFiles("/opt/irisapp/fhirdata/", "FHIRServer", appKey)

    do $System.OBJ.LoadDir("/opt/irisapp/src","ck",,1)

    zn "%SYS"
    Do ##class(Security.Users).UnExpireUserPasswords("*")

    zn "FHIRSERVER"
    zpm "load /opt/irisapp/ -v":1:1

    //zpm "install fhir-portal"
    halt

En utilisant la classe utilitaire HS.FHIRServer.Installer, vous pouvez créer votre serveur FHIR.

Étape 2 - Utilisez l'API FHIR REST ou ObjectScript pour lire, mettre à jour, supprimer et trouver des données FHIR

Je préfère utiliser la classe ObjectScript HS.FHIRServer.Service pour faire toutes les opérations CRUD.

Pour obtenir toutes les données FHIR provenant d'un type de ressource (comme le questionnaire) :

/// Retreive all the records of questionnaire
ClassMethod GetAllQuestionnaire() As %Status
{

    set tSC = $$$OK
    Set %response.ContentType = ..#CONTENTTYPEJSON
    Set %response.Headers("Access-Control-Allow-Origin")="*"

    Try {
        set fhirService = ##class(HS.FHIRServer.Service).EnsureInstance(..#URL)
        set request = ##class(HS.FHIRServer.API.Data.Request).%New()
        set request.RequestPath = "/Questionnaire/"
        set request.RequestMethod = "GET"
        do fhirService.DispatchRequest(request, .pResponse)
        set json = pResponse.Json
        set resp = []
        set iter = json.entry.%GetIterator()
        while iter.%GetNext(.key, .value) { 
          do resp.%Push(value.resource)
        }
        
        write resp.%ToJSON()    
    } Catch Err {
        set tSC = 1
        set message = {}
        set message.type= "ERROR"
        set message.details = "Error on get all questionnairies"       
    }
    
    Quit tSC
}

Pour obtenir un élément de données spécifique du référentiel de données FHIR (comme une occurrence de questionnaire) :

/// Retreive a questionnaire by id
ClassMethod GetQuestionnaire(id As %String) As %Status
{

    set tSC = $$$OK
    Set %response.ContentType = ..#CONTENTTYPEJSON
    Set %response.Headers("Access-Control-Allow-Origin")="*"

    Try {
        set fhirService = ##class(HS.FHIRServer.Service).EnsureInstance(..#URL)
        set request = ##class(HS.FHIRServer.API.Data.Request).%New()
        set request.RequestPath = "/Questionnaire/"_id
        set request.RequestMethod = "GET"
        do fhirService.DispatchRequest(request, .pResponse)
        write pResponse.Json.%ToJSON()    
    } Catch Err {
        set tSC = 1
        set message = {}
        set message.type= "ERROR"
        set message.details = "Error on get the questionnaire"       
    }
    
    Quit tSC
}

Pour créer une nouvelle occurrence de ressource FHIR (comme un nouveau questionnaire) :

/// Create questionnaire
ClassMethod CreateQuestionnaire() As %Status
{
  set tSC = $$$OK
  Set %response.ContentType = ..#CONTENTTYPEJSON
  Set %response.Headers("Access-Control-Allow-Origin")="*"

  Try {
    set fhirService = ##class(HS.FHIRServer.Service).EnsureInstance(..#URL)
    set request = ##class(HS.FHIRServer.API.Data.Request).%New()
    set request.RequestPath = "/Questionnaire/"
    set request.RequestMethod = "POST"
    set data = {}.%FromJSON(%request.Content)
    set data.resourceType = "Questionnaire"
    set request.Json = data
    do fhirService.DispatchRequest(request, .response)
    write response.Json.%ToJSON()
  } Catch Err {
    set tSC = 1
    set message = {}
    set message.type= "ERROR"
    set message.details = "Error on create questionnaire"
  }
  
  Return tSC
}

Pour mettre à jour une ressource FHIR (comme un questionnaire) :

/// Update a questionnaire
ClassMethod UpdateQuestionnaire(id As %String) As %Status
{
  set tSC = $$$OK
  Set %response.ContentType = ..#CONTENTTYPEJSON
  Set %response.Headers("Access-Control-Allow-Origin")="*"

  Try {
    set fhirService = ##class(HS.FHIRServer.Service).EnsureInstance(..#URL)
    set request = ##class(HS.FHIRServer.API.Data.Request).%New()
    set request.RequestPath = "/Questionnaire/"_id
    set request.RequestMethod = "PUT"
    set data = {}.%FromJSON(%request.Content)
    set data.resourceType = "Questionnaire"
    set request.Json = data
    do fhirService.DispatchRequest(request, .response)
    write response.Json.%ToJSON()
  }Catch Err {
    set tSC = 1
    set message = {}
    set message.type= "ERROR"
    set message.details = "Error on update questionnaire"
  }
  Return tSC
}

Pour supprimer un occurrence de ressource FHIR (comme un questionnaire) :

/// Delete a questionnaire by id
ClassMethod DeleteQuestionnaire(id As %String) As %Status
{

    set tSC = $$$OK
    Set %response.ContentType = ..#CONTENTTYPEJSON
    Set %response.Headers("Access-Control-Allow-Origin")="*"

    Try {
        set fhirService = ##class(HS.FHIRServer.Service).EnsureInstance(..#URL)
        set request = ##class(HS.FHIRServer.API.Data.Request).%New()
        set request.RequestPath = "/Questionnaire/"_id
        set request.RequestMethod = "DELETE"
        do fhirService.DispatchRequest(request, .pResponse)
    } Catch Err {
        set tSC = 1
        set message = {}
        set message.type= "ERROR"
        set message.details = "Error on delete the questionnaire"       
    }
    
    Quit tSC
}

Comme vous pouvez le voir, pour créer, il faut utiliser POST, pour mettre à jour, il faut utiliser PUT, pour supprimer, il faut utiliser DELETE et pour lancer une requête, il faut utiliser le verbe GET.

Étape 3 - Créez un client en Angular pour utiliser votre application de serveur FHIR.

J'ai créé une application angulaire en utilisant PrimeNG et en installant le paquet npm install --save @types/fhir. Ce paquet a tous les types FHIR mappé à TypeScript.

Classe de contrôleur en Angular :

import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Period, Questionnaire } from 'fhir/r4';
import { ConfirmationService, MessageService, SelectItem } from 'primeng/api';
import { QuestionnaireService } from './questionnaireservice';

const QUESTIONNAIREID = 'questionnaireId';

@Component({
    selector: 'app-questionnaire',
    templateUrl: './questionnaire.component.html',
    providers: [MessageService, ConfirmationService],
    styleUrls: ['./questionnaire.component.css'],
    encapsulation: ViewEncapsulation.None
})
export class QuestionnaireComponent implements OnInit {

    public questionnaire: Questionnaire;
    public questionnairies: Questionnaire[];
    public selectedQuestionnaire: Questionnaire;
    public questionnaireId: string;
    public sub: any;
    public publicationStatusList: SelectItem[];
    
    constructor(
        private questionnaireService: QuestionnaireService,
        private router: Router,
        private route: ActivatedRoute,
        private confirmationService: ConfirmationService,
        private messageService: MessageService){
            this.publicationStatusList = [
                {label: 'Draft', value: 'draft'},
                {label: 'Active', value: 'active'},
                {label: 'Retired', value: 'retired'},
                {label: 'Unknown', value: 'unknown'}
            ]
        }

    ngOnInit() {
        this.reset();
        this.listQuestionnaires();
        this.sub = this.route.params.subscribe(params => {

            this.questionnaireId = String(+params[QUESTIONNAIREID]);

            if (!Number.isNaN(this.questionnaireId)) {
                this.loadQuestionnaire(this.questionnaireId);
            }
        });
    }

    private loadQuestionnaire(questionnaireId) {
        this.questionnaireService.load(questionnaireId).subscribe(response => {
            this.questionnaire = response;
            this.selectedQuestionnaire = this.questionnaire;
            if(!response.effectivePeriod) {
                this.questionnaire.effectivePeriod = {};
            }
        }, error => {
            console.log(error);
            this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Error on load questionnaire.' });
        });
    }


    public loadQuestions() {
        if(this.questionnaire && this.questionnaire.id) {
            this.router.navigate(['/question', this.questionnaire.id]);
        } else {
            this.messageService.add({ severity: 'warn', summary: 'Warning', detail: 'Choose a questionnaire.' });
        }
    }

    private listQuestionnaires() {
        this.questionnaireService.list().subscribe(response => {
            this.questionnairies = response;
            this.reset();
        }, error => {
            console.log(error);
            this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Error on load the questionnaries.' });
        });
    }

    public onChangeQuestionnaire() {
        if (this.selectedQuestionnaire && !this.selectedQuestionnaire.id) {
            this.messageService.add({ severity: 'warn', summary: 'Warning', detail: 'Select a questionnaire.' });
        } else {
            if(this.selectedQuestionnaire && this.selectedQuestionnaire.id) {
                this.loadQuestionnaire(this.selectedQuestionnaire.id);
            }
        }
    }

    public reset() {
        this.questionnaire = {};
        this.questionnaire.effectivePeriod = {};
    }


    public save() {

        if(this.questionnaire.id && this.questionnaire.id != "") {
            this.questionnaireService.update(this.questionnaire).subscribe(
                (resp) => {
                    this.messageService.add({
                        severity: 'success',
                        summary: 'Success', detail: 'Questionnaire saved.'
                    });
                    this.listQuestionnaires()
                    this.loadQuestionnaire(this.questionnaire.id);
                },
                error => {
                    console.log(error);
                    this.messageService.add({
                        severity: 'error',
                        summary: 'Error', detail: 'Error on save the questionnaire.'
                    });
                }
            );
        } else {
            this.questionnaireService.save(this.questionnaire).subscribe(
                (resp) => {
                    this.messageService.add({
                        severity: 'success',
                        summary: 'Success', detail: 'Questionnaire saved.'
                    });
                    this.listQuestionnaires()
                    this.loadQuestionnaire(resp.id);
                },
                error => {
                    console.log(error);
                    this.messageService.add({
                        severity: 'error',
                        summary: 'Error', detail: 'Error on save the questionnaire.'
                    });
                }
            );
        }
        
    }
    
    public delete(id: string) {

        if (!this.questionnaire || !this.questionnaire.id) {
            this.messageService.add({ severity: 'warn', summary: 'Warning', detail: 'Select a questionnaire.' });
        } else {
            this.confirmationService.confirm({
                message: 'Do you confirm?',
                accept: () => {
                    this.questionnaireService.delete(id).subscribe(
                        () => {
                            this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Questionnaire deleted.' });
                            this.listQuestionnaires();
                            this.reset();
                        },
                        error => {
                            console.log(error);
                            this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Error on delete questionnaire.' });
                        }
                    );
                }
            });
        }
    }
   
}

Fichier HTML Angular

Classe de Service Angular

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { take } from 'rxjs/operators';
import { Questionnaire } from 'fhir/r4';

@Injectable({
  providedIn: 'root'
})
export class QuestionnaireService {

  private url = environment.host2 + 'questionnaire';

  constructor(private http: HttpClient) { }

  public save(Questionnaire: Questionnaire): Observable {
    return this.http.post(this.url, Questionnaire).pipe(take(1));
  }

  public update(Questionnaire: Questionnaire): Observable {
    return this.http.put(`${this.url}/${Questionnaire.id}`, Questionnaire).pipe(take(1));
  }

  public load(id: string): Observable {
    return this.http.get(`${this.url}/${id}`).pipe(take(1));
  }

  public delete(id: string): Observable {
    return this.http.delete(`${this.url}/${id}`).pipe(take(1));
  }

  public list(): Observable {
    return this.http.get(this.url).pipe(take(1));
  }

}

Step 4 - Application in action

  1. Aller à l'application https://openexchange.intersystems.com/package/FHIR-Questionnaires.

2. Clone/git dépose le dépôt dans n'importe quel répertoire local.

$ git clone https://github.com/yurimarx/fhir-questions.git

3. Ouvrir le terminal dans ce répertoire et exécutez :

$ docker-compose up -d
  1. Ouvrir l'application web : http://localhost:52773/fhirquestions/index.html

Voici quelques illustrations :

0
0 101
Article Lorenzo Scalese · Déc 7, 2022 9m read

Salut la communauté,

J’aimerais profiter de notre sujet sur la capture pour les Entrepôts de Données de Santé (EDS) pour vous présenter comment créer rapidement des clients HTTP SOAP et REST.  IRIS ainsi que des applications disponibles sur Open Exchange proposent des solutions permettant de les générer à partir d’un WSDL ou d’une spécification swagger.

Client SOAP

Pour créer un client SOAP, rien de plus simple, vous avez juste besoin du WSDL.  Un assistant est disponible depuis le Studio IRIS,  il permet de générer vos classes pour un client web service, mais également les business services et business operations si vous souhaitez le consommer avec le framework d'interopérabilité.

0
0 141
Article Lorenzo Scalese · Oct 10, 2022 3m read

L'interopérabilité des soins de santé permet d'améliorer les soins aux patients, de réduire les coûts des prestataires de soins et de fournir une image plus précise aux prestataires. Cependant, avec un si grand nombre de systèmes différents, les données sont formatées de nombreuses manières différentes. De nombreuses normes ont été créées pour tenter de résoudre ce problème, notamment HL7v2, HL7v3 et CDA, mais toutes présentent des inconvénients.

FHIR (Fast Healthcare Interoperability Resources), ou Ressources rapides d'interopérabilité des soins de santé, est un nouveau format pour les échanges des informations médicales qui vise à résoudre ces problèmes. Il est développé par Health Level Seven International (HL7), une organisation qui a également développé HL7v2, HL7v3 et CDA.

Aujourd'hui nous allons explorer comment créer et valider une ressource FHIR en utilisant le schéma FHIR à l'aide d'IntelliSense et de la fonctionnalité de complétion automatique dans VS Code.

Etape 1 : Téléchargement du fichier de schéma JSON pour la validation des ressources sur le site officiel de FHIR https://www.hl7.org/fhir/.

Étape 2: Création d'un dossier (dans cet exemple, j'utilise le dossier Patient et la ressource Patient) et copiage du fichier fhir.schema.json extrait dans le même dossier, puis ouverture du dossier à partir du code VS. 

 

Étape 3: Configurez le code VS pour reconnaître le schéma FHIR en modifiant le fichier setting.json.
Appuyez sur CTRL+SHIFT+P et tapez les paramètres de l'espace de travail JSON
 

Étape 4: Création d'un nouveau fichier patient.fhir.json dans le même dossier.
Appuyez sur Ctrl+Espace et vous obtiendrez tous les attributs des ressources FHIR à travers IntelliSense

#

Ajoutez le type de ressource Patient et tous les attributs liés à la ressource Patient vont apparaître dans l'IntelliSense.

VS Code validera automatiquement la structure et la syntaxe de la ressource.


 

Avec l'aide d'IntelliSense et de la fonction de complétion automatique, nous avons créé et validé notre ressource patient.

Step 5: Affichez la ressource créée dans le serveur FHIR d'InterSystems en utilisant l'API Rest à partir de postman

Récupérer la ressource patient créée en utilisant la méthode "Get"

Félicitations, nous avons créé, validé notre ressource patient et réussi à l'envoyer et la récupérer sur le serveur FHIR d'InterSystems en utilisant postman.
De cette façon, nous pouvons facilement créer et valider n'importe quelle ressource FHIR.

0
0 456