Article
· Avr 9 5m de lecture

Connexion entre InterSystems IRIS et Firebase Cloud Firestore

J'ai récemment eu besoin de surveiller depuis HealthConnect les enregistrements présents dans une base de données NoSQL dans le Cloud, plus précisément Cloud Firestore, déployé dans Firebase. D'un coup d'œil rapide, j'ai pu voir à quel point il serait facile de créer un adaptateur ad-hoc pour établir la connexion en tirant parti des capacités d'Embedded Python, et je me suis donc mis au travail.

Préparation de l'environnement

Pour commencer, nous avons besoin d'une instance de la base de données sur laquelle nous pouvons effectuer les tests. En accédant à la console Firebase, nous avons créé un nouveau projet auquel nous avons ajouté la base de données Firestore.

Ensuite, nous créons une collection sur notre base de données appelée data_poc et dans laquelle nous incluons 3 documents que nous récupérerons plus tard de notre production.

Avec la base de données déployée, nous allons obtenir notre fichier json avec les clés nécessaires pour faire la connexion depuis notre production dans IRIS. Pour ce faire, depuis la console de notre projet Firebase nous ouvrons la page des services de compte que nous trouvons depuis la configuration du projet (Project Settings -> Service Accounts) et nous générons une nouvelle clé privée :

Ceci téléchargera un fichier json que nous placerons dans un chemin sur notre serveur, dans cet exemple nous le laisserons dans le dossier /shared/durable de notre Docker.

Creating the adapter

Afin de pouvoir se connecter à notre base de données dans Firebase, nous allons devoir créer un adaptateur spécifique qui établira la connexion. Comme nous l'avons mentionné précédemment, nous allons utiliser les capacités que nous offre Embedded Python, nous allons donc installer la bibliothèque qui nous permet de nous connecter, firebase-admin

Avec notre bibliothèque déjà installée, nous pouvons maintenant créer notre adaptateur :

Class Local.Adapter.FirebaseInboundAdapter Extends Ens.InboundAdapter
{

Property KeyPath As %String(MAXLEN = 100);
Property DocName As %String(MAXLEN = 100);
Parameter SETTINGS = "KeyPath,DocName";
Method OnTask() As %Status
{
    $$$TRACE("Connecting")
    set tSC = $$$OK
    set listOfDocs = ##class("%Library.ListOfDataTypes").%New()
    if ('$DATA(^$GLOBAL("^LASTFIREBASE"))) {
        set ^LASTFIREBASE(..DocName) = 0
    }
    
    do ..ConnectAndQuery(..KeyPath, ^LASTFIREBASE(..DocName), listOfDocs, ..DocName)

    for i = 1:1:listOfDocs.Count() {
        set msg = ##class(Local.Message.FirebaseDocRequest).%New()
        set msg.Doc = listOfDocs.GetAt(i)
        set tSC=..BusinessHost.ProcessInput(msg)
        set docRead = ##class(%DynamicAbstractObject).%FromJSON(msg.Doc)
        set ^LASTFIREBASE(..DocName) = docRead.id
        $$$TRACE("Index: "_^LASTFIREBASE(..DocName))
    }
    $$$TRACE("Finishing connection")

    Quit tSC
}

/// Using Embedded Python to connect with Firebase
ClassMethod ConnectAndQuery(keyPath As %String, lastId As %String, ByRef listOfDocs As %List, docName As %String) [ Language = python ]
{
        import iris
        import firebase_admin
        from firebase_admin import credentials
        from firebase_admin import firestore

        if not firebase_admin._apps:
            cred = credentials.Certificate(keyPath)
            firebase_admin.initialize_app(cred)

        db = firestore.client()

        # Read Data
        docs_refer = db.collection(docName)
        docs = docs_refer.where("id",">",lastId).stream()
        # docs = docs_refer.stream()

        for doc in docs:
            # listOfDocs.Insert(doc.to_dict())            
            listOfDocs.Insert(str(doc.to_dict()).replace("'", '"'))
        return 1
}

}

Comme vous pouvez le voir, nous utilisons un champ d'identification id comme critère pour obtenir les derniers documents enregistrés dans le système, pour chaque lecture d'un nouveau document nous prenons son identifiant et le stockons dans un global. Nous avons inclus deux paramètres dans l'adaptateur :

  • KeyPath: où nous indiquons le chemin et le nom du fichier json qui contient les clés pour accéder à notre base de données dans Firebase.
  • DocName: dans lequel nous définissons le nom de la collection ou du document que nous stockons dans la base de données. Pour chaque type de collection, nous devons ajouter un nouveau business service en modifiant ce paramètre :

Avec les clés de connexion, nous pouvons maintenant nous connecter à la base de données en utilisant la fonction développée à l'aide de la fonctionnalité Embedded Python. Nous avons préalablement installé la bibliothèque firebase-admin qui nous permettra de gérer la connexion et que vous pouvez trouver dans le fichier requirements.txt du projet associé. Une fois la connexion établie, nous récupérerons tous les documents dont l'identifiant est postérieur au dernier document lu. Nous insérerons les documents récupérés dans une liste de chaînes de caractères que nous numériserons et enverrons ensuite à notre business service.

# Read Data
docs_refer = db.collection(docName)
docs = docs_refer.where("id",">",lastId).stream()
# docs = docs_refer.stream()

for doc in docs:
    listOfDocs.Insert(str(doc.to_dict()).replace("'", '"'))

Avec l'adaptateur développé, il nous suffit de mettre en œuvre notre Business Service pour pouvoir l'utiliser dans notre production :

Class Local.BS.FirebaseBS Extends Ens.BusinessService
{

Parameter ADAPTER = "Local.Adapter.FirebaseInboundAdapter";
Method OnProcessInput(pRequest As Local.Message.FirebaseDocRequest, pResponse As %RegisteredObject) As %Status
{
        $$$TRACE(pRequest.Doc)

        Quit $$$OK
}

}

Pour notre exemple, nous définirons uniquement l'écriture d'une trace avec le message reçu de l'adaptateur, le message aura uniquement une propriété de type String que nous avons appelée Doc et qui contiendra le document récupéré.

Test de l'adaptateur

Nous avons déjà tout ce qu'il faut pour récupérer des documents dans notre base de données, il ne nous reste donc plus qu'à configurer notre production.

C'est parfait ! Nous avons déjà configuré notre BS pour qu'il fasse des requêtes à la base de données toutes les 5 secondes, lançons la production et vérifions le log :

Voici nos documents, voyons maintenant ce qui se passe si nous en ajoutons un nouveau à notre base de données :

Voilà notre nouvel enregistrement. Voilà, c'est prêt, nous avons maintenant notre adaptateur qui fonctionne.
Voilà, c'est tout pour l'article d'aujourd'hui, si vous avez des questions ou des suggestions, n'hésitez pas à les écrire dans les commentaires.

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer