Article
· Sept 22, 2023 8m de lecture

Stratégie Python pour Iris FHIR

Description

Avec le Serveur InterSystems IRIS FHIR, vous pouvez construire une stratégie pour personnaliser le comportement du serveur (pour plus de détails, consultez documentation).

Image

Ce référentiel contient une stratégie Python qui peut être utilisée comme point de départ pour construire votre propre stratégie en Python.

Cette stratégie de démonstration présente les caractéristiques suivantes :

  • Mettre à jour la déclaration de capacité pour supprimer la ressource Account (compte).
  • Simuler un système de gestion de consentement pour accorder ou non l'accès à la ressource Observation.
    • Si l'utilisateur a des droits suffisants, la ressource Observation est renvoyée.
    • Sinon, la ressource Observation n'est pas renvoyée.

Installation

Étapes d'installation

  1. Clonez le référentiel suivant
git clone git@github.com:grongierisc/iris-fhir-python-strategy.git
  1. Construisez l'image docker
docker-compose build
  1. Lancez l'image docker
docker-compose up -d
  1. Ouvrez le serveur FHIR dans votre navigateur
GET http://localhost:8083/fhir/r4/metadata
Accept: application/json+fhir

La ressource Account (compte) ne peut pas figurer dans la Déclaration de capacité.

GET http://localhost:8083/fhir/r4/Account
Accept: application/json+fhir

retours :

{
  "resourceType": "OperationOutcome",
  "issue": [
    {
      "severity": "error",
      "code": "not-supported",
      "diagnostics": "<HSFHIRErr>ResourceNotSupported",
      "details": {
        "text": "Resource type 'Account' is not supported."
      }
    }
  ]
}
  1. Ouvrez le compte d'un patient sans authentification (vous ne devez pas avoir accès à l'Observation)
GET http://localhost:8083/fhir/r4/Observation?patient=178
Content-Type: application/json+fhir
Accept: application/json+fhir

retours :

{
  "resourceType": "Bundle",
  "id": "feaa09c0-1cb7-11ee-b77a-0242c0a88002",
  "type": "searchset",
  "timestamp": "2023-07-07T11:07:49Z",
  "total": 0,
  "link": [
    {
      "relation": "self",
      "url": "http://localhost:8083/fhir/r4/Observation?patient=178"
    }
  ]
}
  1. Ouvrez le compte d'un patient avec authentification (vous devez avoir accès à l'Observation).
GET http://localhost:8083/fhir/r4/Observation?patient=178
Content-Type: application/json+fhir
Accept: application/json+fhir
Authorization: Basic U3VwZXJVc2VyOlNZUw==

retours :

{
  "resourceType": "Bundle",
  "id": "953a1b06-1cb7-11ee-b77b-0242c0a88002",
  "type": "searchset",
  "timestamp": "2023-07-07T11:08:04Z",
  "total": 100,
  "link": [
    {
      "relation": "self",
      "url": "http://localhost:8083/fhir/r4/Observation?patient=178"
    }
  ],
  "entry": [
    {
      "fullUrl": "http://localhost:8083/fhir/r4/Observation/277",
      "resource": {
        "resourceType": "Observation",
        "id": "277",
        "status": "final",
        "category": [
          ...
        ],
      }
    },
    ...
  ]
}

Vous trouverez plus de détails dans la section suivante "Consentement".

Consentement

Le système de gestion de consentement est simulé par la méthode consent (consentement) de la classe CustomInteraction (interaction personnalisée) du module custom (personnalisation).

La méthode consent retourne True (vrai) si l'utilisateur a les droits suffisants pour accéder à la ressource, False (faux) dans le cas contraire.

    def consent(self, resource_type, user, roles):
        #Exemple de logique de consentement: autoriser uniquement les utilisateurs ayant le rôle "%All" à consulter
        #les ressources "Observation".
        if resource_type == 'Observation':
            if '%All' in roles:
                return True
            else:
                return False
        else:
            return True

La fonction consent fait partie de la classe CustomInteraction.

La classe CustomInteraction est une implémentation de la classe Interaction.

La classe Interaction est une classe abstraite qui doit être implémentée par la stratégie. Elle fait partie du module FhirInteraction.

class Interaction(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def on_before_request(self,
                          fhir_service:'iris.HS.FHIRServer.API.Service',
                          fhir_request:'iris.FHIRServer.API.Data.Request',
                          body:dict,
                          timeout:int):
        """
        on_before_request est appelé avant que la demande ne soit envoyée au serveur.
        param fhir_service: objet service fhir  iris.HS.FHIRServer.API.Service
        param fhir_request: objet service fhir  iris.FHIRServer.API.Data.Request
        param timeout: le délai en secondes
        return: None
        """


    @abc.abstractmethod
    def on_after_request(self,
                         fhir_service:'iris.HS.FHIRServer.API.Service',
                         fhir_request:'iris.FHIRServer.API.Data.Request',
                         fhir_response:'iris.FHIRServer.API.Data.Response',
                         body:dict):
        """
        on_after_request est appelée après l'envoi de la demande au serveur.
        param fhir_service: objet service fhir  iris.HS.FHIRServer.API.Service
        param fhir_request: objet requête fhir iris.FHIRServer.API.Data.Request
        param fhir_response: objet réponse fhir iris.FHIRServer.API.Data.Response
        return: None
        """


    @abc.abstractmethod
    def post_process_read(self,
                          fhir_object:dict) -> bool:
        """
        post_process_read est appelée après l'opération de lecture.
        param fhir_object: objet fhir
        return: "True" la ressource doit être renvoyée au client, "False" dans le cas contraire
        """


    @abc.abstractmethod
    def post_process_search(self,
                            rs:'iris.HS.FHIRServer.Util.SearchResult',
                            resource_type:str):
        """
        post_process_search est appelée une fois l'opération de recherche terminée.
        param rs: résultat de recherche iris.HS.FHIRServer.Util.SearchResult
        param resource_type: type de ressource
        return: None
        """

La classe CustomInteraction est une implémentation de la classe Interaction.

class CustomInteraction(Interaction):

    def on_before_request(self, fhir_service, fhir_request, body, timeout):
        #Extraction de l'utilisateur et des rôles pour cette demande
        #afin que le consentement puisse être évalué.
        self.requesting_user = fhir_request.Username
        self.requesting_roles = fhir_request.Roles

    def on_after_request(self, fhir_service, fhir_request, fhir_response, body):
        #Suppression de l'utilisateur et des rôles entre les demandes.
        self.requesting_user = ""
        self.requesting_roles = ""

    def post_process_read(self, fhir_object):
        #Évaluation du consentement en fonction de la ressource et de l'utilisateur/des rôles.
        #La valeur 0 indique que cette ressource ne doit pas être affichée - un message 404 Not Found (404 introuvable)
        #sera renvoyé à l'utilisateur.
        return self.consent(fhir_object['resourceType'],
                        self.requesting_user,
                        self.requesting_roles)

    def post_process_search(self, rs, resource_type):
        #Itération sur chaque ressource de l'ensemble de recherche et évaluation
        #du consentement en fonction de la ressource et de l'utilisateur/des rôles.
        #Chaque ligne marquée comme supprimée et sauvegardée sera exclue de l'ensemble.
        rs._SetIterator(0)
        while rs._Next():
            if not self.consent(rs.ResourceType,
                            self.requesting_user,
                            self.requesting_roles):
                #Sélection de la ligne comme supprimée et sauvegarde.
                rs.MarkAsDeleted()
                rs._SaveRow()

    def consent(self, resource_type, user, roles):
        #Exemple de logique de consentement: autoriser uniquement les utilisateurs ayant le rôle "%All" à consulter
        #les ressources "Observation".
        if resource_type == 'Observation':
            if '%All' in roles:
                return True
            else:
                return False
        else:
            return True

Vous pouvez modifier le module custom pour implémenter votre propre logique de consentement.

Toutes les modifications apportées au module custom seront directement reflétées dans le serveur FHIR.

Il est possible d'implémenter d'autres comportements en surchargeant les classes Interaction.

  • WIP

Personnalisation de la ressource CapabilityStatement

Le serveur IRIS FHIR fournit une ressource CapabilityStatement par défaut en fonction du Guide d'implémentation fourni au moment de l'installation.

De plus amples informations sur la manière de personnaliser la Resource CapabilityStatement sont disponibles à l'adresse FHIR CapabilityStatement.

Pour cet exemple, le Guide d'implémentation est FHIR R4 brut.

Pour personnaliser la ressource CapabilityStatement, vous pouvez modifier le module custom.

La classe CustomStrategy est une implémentation de la classe Strategy(stratégie).

La classe Strategy est une classe abstraite qui doit être implémentée par la stratégie. Elle fait partie du module FhirInteraction.

class Strategy(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def on_get_capability_statement(self,capability_statement:dict)-> dict:
        """
        on_after_get_capability_statement est appelé une fois la déclaration de capacité récupérée.
        param capability_statement: la déclaration de capacité
        return: None
        """

La méthode on_get_capability_statement est appelée après la génération de la ressource CapabilityStatement.

La classe CustomStrategy est une implémentation de la classe Strategy.

class CustomStrategy(Strategy):

    def on_get_capability_statement(self, capability_statement):
        # Exemple : Compte de ressources
        capability_statement['rest'][0]['resource'] = [resource for resource in capability_statement['rest'][0]['resource'] if resource['type'] != 'Account']
        return capability_statement

Vous pouvez modifier le module custom pour implémenter votre propre Custom CapabilityStatement.

Pour appliquer les modifications, vous devez mettre à jour la configuration du serveur fhir.

cd /irisdev/app/src/python
/usr/irissys/bin/irispython
>>> import custom
>>> custom.set_capability_statement()
Discussion (0)0
Connectez-vous ou inscrivez-vous pour continuer