Artículo
· 4 nov, 2023 Lectura de 5 min

Conectando InterSystems IRIS y Cloud Firestore de Firebase

Recientemente me comentaron sobre la necesidad de monitorizar desde HealthConnect los registros presentes en una base de datos NoSQL en el Cloud, más en concreto Cloud Firestore, desplegada en Firebase. Con un rápido vistazo pude ver lo sencillo que resultaría crear un Adapter ad-hoc para realizar la conexión aprovechando las capacidades de Embedded Python, así que me puse manos a la obra.

Preparando el entorno

Para poder comenzar necesitamos una instancia de dicha base de datos sobre la que poder realizar las pruebas, accediendo a la consola de Firebase hemos creado un nuevo proyecto sobre el que a su vez añadimos la base de datos Firestore.

A continuación y sobre nuestra nueva base de datos creamos una colección que denominados data_poc y en la cual incluimos 3 documentos que posteriormente recuperaremos desde nuestra producción.

Con la base de datos desplegada vamos a obtener nuestro archivo json con las claves necesarias para realizar la conexión desde nuestra producción en IRIS. Para ello desde la consola de nuestro proyecto de Firebase abrimos la página de servicios de la cuenta que encontramos desde la configuración del proyecto (Project Settings -> Service Accounts) y generamos una nueva clave privada:

Esto nos descargará un fichero json que ubicaremos en una ruta de nuestro servidor, en este ejemplo lo dejaremos en la carpeta /shared/durable de nuestro Docker

Creación del adaptador

Para que nuestra producción sea capaz de conectarse con nuestra base de datos en Firebase deberemos crear un adaptador específico que realice la conexión, como comentamos anteriormente utilizaremos las capacidades que nos ofrece Embedded Python, por lo que instalaremos la librería que nos permite la conexión, firebase-admin.

Con nuestra librería ya instalada ya podemos crear nuestro adaptador:

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
}

}

Como podéis ver estamos usando un campo identificador id como criterio para obtener los últimos documentos registrados en el sistema, por cada lectura de un nuevo documento tomamos su identificador y lo almacenamos en un global. Hemos incluido dos parámetros en el adaptador:

  • KeyPath: donde indicaremos la ruta y el nombre del archivo json que contiene las claves para acceder a nuestra base de datos en Firebase.
  • DocName: en el que definimos el nombre de la colección o documento que almacenamos en la base de datos. Por cada tipo de colección deberemos añadir un nuevo Business Service modificando este parámetro:

Con las claves de la conexión ya podemos conectarnos a la base de datos mediante la función desarrollada mediante la funcionalidad de Embedded Python. Hemos instalado previamente la librería firebase-admin que nos va a permitir gestionar la conexión y que podéis encontrar en el fichero requirements.txt del proyecto asociado. Una vez realizada la conexión recuperaremos todos los documentos con un identificador posterior al último que leímos. Insertaremos los documentos recuperados en una lista de tipo string que posteriormente recorreremos y enviaremos a nuestro 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("'", '"'))

Con el adaptador desarrollado sólo necesitamos implementar nuestro Business Service para poder utilizarlo en nuestra producción:

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
}

}

Para nuestro ejemplo sólo definiremos la escritura de una traza con el mensaje recibido desde el adaptador, dicho mensaje únicamente contará con una propiedad de tipo String que hemos llamado Doc y que contendrá el documento recuperado.

Probando el adaptador

Ya tenemos todo necesario para recuperar documentos de nuestra base de datos, por lo que sólo necesitamos configurar nuestra producción.

¡Perfecto! Ya tenemos nuestro BS configurado para que realice consultas cada 5 segundos a la base de datos, arranquemos la producción y veamos el log:

Aqui tenemos nuestros documentos, veamos ahora que pasa si añadimos uno nuevo a nuestra base de datos:

Ahí tenemos nuestro nuevo registro. Pues listo ya tenemos nuestro adaptador funcionando.

Pues hasta aquí el artículo de hoy, si tenéis alguna pregunta o sugerencia no dudéis en escribirla en los comentarios

Comentarios (0)1
Inicie sesión o regístrese para continuar