Comience a crear una extensión

Esta página lo guía a través de los pasos necesarios para crear una extensión de Firebase simple, que puede instalar en sus proyectos o compartir con otros. Este ejemplo simple de una extensión de Firebase observará su base de datos en tiempo real en busca de mensajes y los convertirá a mayúsculas.

1. Configure su entorno e inicialice un proyecto.

Antes de poder comenzar a crear una extensión, deberá configurar un entorno de compilación con las herramientas necesarias.

  1. Instale Node.js 16 o posterior. Una forma de instalar Node es mediante nvm (o nvm-windows ).

  2. Instale o actualice a la última versión de Firebase CLI . Para instalar o actualizar usando npm , ejecute este comando:

    npm install -g firebase-tools
    

Ahora use Firebase CLI para inicializar un nuevo proyecto de extensión:

  1. Cree un directorio para su extensión y cd en él:

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
    
  2. Ejecute el comando ext:dev:init de Firebase CLI:

    firebase ext:dev:init
    

    Cuando se le solicite, elija JavaScript como lenguaje para las funciones (pero tenga en cuenta que también puede usar TypeScript cuando desarrolle su propia extensión) y, cuando se le solicite instalar dependencias, responda "sí". (Acepte los valores predeterminados para cualquier otra opción). Este comando configurará una base de código esqueleto para una nueva extensión, desde la cual puede comenzar a desarrollar su extensión.

2. Pruebe la extensión de ejemplo usando el emulador.

Cuando Firebase CLI inicializó el nuevo directorio de extensiones, creó una función de ejemplo simple y un directorio integration-tests que contiene los archivos necesarios para ejecutar una extensión usando el conjunto de emuladores de Firebase.

Intente ejecutar la extensión de ejemplo en el emulador:

  1. Cambie al directorio integration-tests :

    cd functions/integration-tests
    
  2. Inicie el emulador con un proyecto de demostración:

    firebase emulators:start --project=demo-test
    

    El emulador carga la extensión en un proyecto "ficticio" predefinido ( demo-test ). Hasta ahora, la extensión consiste en una única función activada por HTTP, greetTheWorld , que devuelve un mensaje de "hola mundo" cuando se accede.

  3. Con el emulador aún ejecutándose, prueba la función greetTheWorld de la extensión visitando la URL que imprimió cuando lo iniciaste.

    Su navegador muestra el mensaje "Hola mundo de saludo al mundo".

  4. El código fuente de esta función se encuentra en el directorio functions de la extensión. Abra el código fuente en el editor o IDE de su elección:

    funciones/index.js

    const functions = require("firebase-functions");
    
    exports.greetTheWorld = functions.https.onRequest((req, res) => {
      // Here we reference a user-provided parameter
      // (its value is provided by the user during installation)
      const consumerProvidedGreeting = process.env.GREETING;
    
      // And here we reference an auto-populated parameter
      // (its value is provided by Firebase after installation)
      const instanceId = process.env.EXT_INSTANCE_ID;
    
      const greeting = `${consumerProvidedGreeting} World from ${instanceId}`;
    
      res.send(greeting);
    });
    
  5. Mientras el emulador se está ejecutando, recargará automáticamente cualquier cambio que realice en su código de Funciones. Intente realizar un pequeño cambio en la función greetTheWorld :

    funciones/index.js

    const greeting = `${consumerProvidedGreeting} everyone, from ${instanceId}`;
    

    Guarde sus cambios. El emulador recargará su código y ahora, cuando visite la URL de la función, verá el saludo actualizado.

3. Agregue información básica a extensión.yaml

Ahora que tiene configurado un entorno de desarrollo y está ejecutando el emulador de extensiones, puede comenzar a escribir su propia extensión.

Como primer paso modesto, edite los metadatos de la extensión predefinida para reflejar la extensión que desea escribir en lugar de greet-the-world . Estos metadatos se almacenan en el archivo extension.yaml .

  1. Abra extension.yaml en su editor y reemplace todo el contenido del archivo con lo siguiente:

    name: rtdb-uppercase-messages
    version: 0.0.1
    specVersion: v1beta  # Firebase Extensions specification version; don't change
    
    # Friendly display name for your extension (~3-5 words)
    displayName: Convert messages to upper case
    
    # Brief description of the task your extension performs (~1 sentence)
    description: >-
      Converts messages in RTDB to upper case
    
    author:
      authorName: Your Name
      url: https://your-site.example.com
    
    license: Apache-2.0  # Required license
    
    # Public URL for the source code of your extension
    sourceUrl: https://github.com/your-name/your-repo
    

    Tenga en cuenta la convención de nomenclatura utilizada en el campo name : las extensiones oficiales de Firebase se nombran con un prefijo que indica el producto principal de Firebase en el que opera la extensión, seguido de una descripción de lo que hace la extensión. Deberías utilizar la misma convención en tus propias extensiones.

  2. Dado que cambió el nombre de su extensión, también debe actualizar la configuración de su emulador con el nuevo nombre:

    1. En functions/integration-tests/firebase.json , cambie greet-the-world a rtdb-uppercase-messages .
    2. Cambie el nombre functions/integration-tests/extensions/greet-the-world.env a functions/integration-tests/extensions/rtdb-uppercase-messages.env .

Todavía quedan algunos restos de la extensión greet-the-world en su código de extensión, pero déjelos por ahora. Los actualizará en las próximas secciones.

4. Escriba una función en la nube y declarela como un recurso de extensión.

Ahora puedes empezar a escribir código. En este paso, escribirá una función en la nube que realice la tarea principal de su extensión, que es monitorear su base de datos en tiempo real en busca de mensajes y convertirlos a mayúsculas.

  1. Abra el código fuente de las funciones de la extensión (en el directorio de functions de la extensión) en el editor o IDE de su elección. Reemplace su contenido con lo siguiente:

    funciones/index.js

    import { database, logger } from "firebase-functions/v1";
    
    const app = initializeApp();
    
    // Listens for new messages added to /messages/{pushId}/original and creates an
    // uppercase version of the message to /messages/{pushId}/uppercase
    // for all databases in 'us-central1'
    export const makeuppercase = database
      .ref("/messages/{pushId}/uppercase")
      .onCreate(async (snapshot, context) => {
        // Grab the current value of what was written to the Realtime Database.
        const original = snapshot.val();
    
        // Convert it to upper case.
        logger.log("Uppercasing", context.params.pushId, original);
        const uppercase = original.toUpperCase();
    
        // Setting an "uppercase" sibling in the Realtime Database.
        const upperRef = snapshot.ref.parent.child("upper");
        await upperRef.set(uppercase);
    });
    

    La función anterior, que reemplazó, era una función activada por HTTP, que se ejecutaba cuando se accedía a un punto final HTTP. La nueva función se activa mediante eventos de la base de datos en tiempo real: busca nuevos elementos en una ruta particular y, cuando detecta uno, escribe la versión mayúscula del valor en la base de datos.

    Por cierto, este nuevo archivo utiliza la sintaxis del módulo ECMAScript ( import y export ) en lugar de CommonJS ( require ). Para usar módulos ES en Node, especifique "type": "module" en functions/package.json :

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      …
    }
    
  2. Cada función en su extensión debe declararse en el archivo extension.yaml . La extensión de ejemplo declaró greetTheWorld como la única función en la nube de la extensión; ahora que lo reemplazó con makeuppercase , también necesita actualizar su declaración.

    Abra extension.yaml y agregue un campo resources :

    resources:
      - name: makeuppercase
        type: firebaseextensions.v1beta.function
        properties:
          eventTrigger:
            eventType: providers/google.firebase.database/eventTypes/ref.create
            # DATABASE_INSTANCE (project's default instance) is an auto-populated
            # parameter value. You can also specify an instance.
            resource: projects/_/instances/${DATABASE_INSTANCE}/refs/messages/{pushId}/original
          runtime: "nodejs18"
    
  3. Dado que su extensión ahora usa Realtime Database como activador, debe actualizar la configuración de su emulador para ejecutar el emulador RTDB junto con el emulador de Cloud Functions:

    1. Si el emulador aún se está ejecutando, deténgalo presionando Ctrl-C.

    2. Desde el directorio functions/integration-tests , ejecute el siguiente comando:

      firebase init emulators
      

      Cuando se le solicite, omita la configuración de un proyecto predeterminado, luego seleccione los emuladores de funciones y bases de datos. Acepte los puertos predeterminados y permita que la herramienta de configuración descargue los archivos necesarios.

    3. Reinicia el emulador:

      firebase emulators:start --project=demo-test
      
  4. Pruebe su extensión actualizada:

    1. Abra la interfaz de usuario del emulador de base de datos utilizando el enlace que el emulador imprimió cuando lo inició.

    2. Edite el nodo raíz de la base de datos:

      • Campo: messages
      • Tipo: json
      • Valor: {"11": {"original": "recipe"}}

      Si todo está configurado correctamente, cuando guarde los cambios de su base de datos, la función makeuppercase de la extensión debería activarse y agregar un registro secundario al mensaje 11 con el contenido "upper": "RECIPE" . Eche un vistazo a los registros y las pestañas de la base de datos de la interfaz de usuario del emulador para confirmar los resultados esperados.

    3. Intente agregar algunos elementos secundarios más al nodo messages ( {"original":"any text"} ). Siempre que agregue un nuevo registro, la extensión debe agregar un campo uppercase que contenga el contenido en mayúscula del campo original .

Ahora tiene una extensión completa, aunque simple, que opera en una instancia RTDB. En las secciones siguientes, perfeccionará esta extensión con algunas características adicionales. Luego, preparará la extensión para distribuirla a otros y, finalmente, aprenderá cómo publicar su extensión en Extensions Hub.

5. Declarar API y roles

Firebase otorga a cada instancia de una extensión instalada acceso limitado al proyecto y sus datos mediante una cuenta de servicio por instancia. Cada cuenta tiene el conjunto mínimo de permisos necesarios para operar. Por este motivo, debe declarar explícitamente cualquier función de IAM que requiera su extensión; Cuando los usuarios instalan su extensión, Firebase crea una cuenta de servicio con estos roles otorgados y la usa para ejecutar la extensión.

No es necesario declarar roles para desencadenar los eventos de un producto, pero sí es necesario declarar un rol para interactuar con él de otra manera. Debido a que la función que agregó en el último paso escribe en Realtime Database, debe agregar la siguiente declaración a extension.yaml :

roles:
  - role: firebasedatabase.admin
    reason: Allows the extension to write to RTDB.

De manera similar, declara las API de Google que utiliza una extensión en el campo apis . Cuando los usuarios instalen su extensión, se les preguntará si desean habilitar automáticamente estas API para su proyecto. Por lo general, esto solo es necesario para las API de Google que no son de Firebase y no es necesario para esta guía.

6. Definir parámetros configurables por el usuario

La función que creó en los dos últimos pasos observó una ubicación RTDB específica para los mensajes entrantes. A veces, lo que realmente desea es observar una ubicación específica, como cuando su extensión opera en una estructura de base de datos que usa exclusivamente para su extensión. Sin embargo, la mayoría de las veces, querrás que estos valores sean configurables por los usuarios que instalen tu extensión en sus proyectos. De esta manera, los usuarios pueden utilizar su extensión para trabajar con la configuración de su base de datos existente.

Haga que la ruta que la extensión busca en busca de mensajes nuevos sea configurable por el usuario:

  1. En el archivo extension.yaml , agregue una sección params :

    - param: MESSAGE_PATH
      label: Message path
      description: >-
        What is the path at which the original text of a message can be found?
      type: string
      default: /messages/{pushId}/original
      required: true
      immutable: false
    

    Esto define un nuevo parámetro de cadena que se pedirá a los usuarios que establezcan cuando instalen su extensión.

  2. Aún en el archivo extension.yaml , regrese a su declaración makeuppercase y cambie el campo resource a lo siguiente:

    resource: projects/_/instances/${DATABASE_INSTANCE}/refs/${param:MESSAGE_PATH}
    

    El token ${param:MESSAGE_PATH} es una referencia al parámetro que acaba de definir. Cuando se ejecute su extensión, este token será reemplazado por cualquier valor que el usuario haya configurado para ese parámetro, con el resultado de que la función makeuppercase escuchará la ruta especificada por el usuario. Puede usar esta sintaxis para hacer referencia a cualquier parámetro definido por el usuario en cualquier lugar de extension.yaml (y en POSTINSTALL.md ; hablaremos de esto más adelante).

  3. También puede acceder a los parámetros definidos por el usuario desde su código de funciones.

    En la función que escribiste en la última sección, codificaste la ruta para observar los cambios. Cambie la definición del activador para que haga referencia al valor definido por el usuario:

    funciones/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate
    

    Tenga en cuenta que en Firebase Extensions, este cambio se realiza únicamente por motivos de documentación: cuando se implementa una función de nube como parte de una extensión, utiliza la definición de activador del archivo extension.yaml e ignora el valor especificado en la definición de función. Sin embargo, es una buena idea documentar en el código de dónde proviene este valor.

  4. Puede que le resulte decepcionante realizar un cambio de código que no tiene efecto en tiempo de ejecución, pero la lección importante que debe aprender es que puede acceder a cualquier parámetro definido por el usuario en su código de función y usarlo como un valor ordinario en la lógica de la función. Como reconocimiento a esta capacidad, agregue la siguiente declaración de registro para demostrar que realmente está accediendo al valor que definió el usuario:

    funciones/index.js

    export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate(
      async (snapshot, context) => {
        logger.log("Found new message at ", snapshot.ref);
    
        // Grab the current value of what was written to the Realtime Database.
        ...
    
  5. Normalmente, a los usuarios se les solicita que proporcionen valores para los parámetros cuando instalan una extensión. Sin embargo, cuando utiliza el emulador para pruebas y desarrollo, se salta el proceso de instalación, por lo que en su lugar proporciona valores para los parámetros definidos por el usuario mediante un archivo env .

    Abra functions/integration-tests/extensions/rtdb-uppercase-messages.env y reemplace la definición GREETING con lo siguiente:

    MESSAGE_PATH=/msgs/{pushId}/original
    

    Observe que la ruta anterior es diferente de la ruta predeterminada y de la ruta que definió anteriormente; Esto es sólo para demostrarte a ti mismo que tu definición está surtiendo efecto cuando pruebes la extensión actualizada.

  6. Ahora, reinicie el emulador y visite nuevamente la interfaz de usuario del emulador de base de datos.

    Edite el nodo raíz de la base de datos, utilizando la ruta que definió anteriormente:

    • Campo: msgs
    • Tipo: json
    • Valor: {"11": {"original": "recipe"}}

    Cuando guarda los cambios de su base de datos, la función makeuppercase de la extensión debería activarse como lo hacía antes, pero ahora también debería imprimir el parámetro definido por el usuario en el registro de la consola.

7. Proporcionar enlaces de eventos para lógica definida por el usuario.

Ya has visto, como autor de una extensión, cómo un producto de Firebase puede activar la lógica proporcionada por la extensión: la creación de nuevos registros en Realtime Database activa la función makeuppercase . Su extensión puede tener una relación análoga con los usuarios que instalan su extensión: su extensión puede activar la lógica que define el usuario .

Una extensión puede proporcionar enlaces sincrónicos , enlaces asincrónicos o ambos. Los enlaces sincrónicos brindan a los usuarios una forma de realizar tareas que bloquean la finalización de una de las funciones de la extensión. Esto puede resultar útil, por ejemplo, para ofrecer a los usuarios una forma de realizar un preprocesamiento personalizado antes de que una extensión haga su trabajo.

En esta guía, agregará un enlace asincrónico a su extensión, que permitirá a los usuarios definir sus propios pasos de procesamiento que se ejecutarán después de que su extensión escriba el mensaje en mayúsculas en Realtime Database. Los enlaces asincrónicos utilizan Eventarc para activar funciones definidas por el usuario. Las extensiones declaran los tipos de eventos que emiten y, cuando los usuarios instalan la extensión, eligen qué tipos de eventos les interesan. Si eligen al menos un evento, Firebase proporcionará un canal Eventarc para la extensión como parte del proceso de instalación. . Luego, los usuarios pueden implementar sus propias funciones en la nube que escuchan en ese canal y se activan cuando la extensión publica nuevos eventos.

Siga estos pasos para agregar un enlace asincrónico:

  1. En el archivo extension.yaml , agregue la siguiente sección, que declara el tipo de evento que emite la extensión:

    events:
      - type: test-publisher.rtdb-uppercase-messages.v1.complete
        description: >-
          Occurs when message uppercasing completes. The event subject will contain
          the RTDB URL of the uppercase message.
    

    Los tipos de eventos deben ser universalmente únicos; Para garantizar la unicidad, nombre siempre sus eventos usando el siguiente formato: <publisher-id>.<extension-id>.<version>.<description> . (Aún no tienes un ID de editor, así que usa test-publisher por ahora).

  2. Al final de la función makeuppercase , agrega código que publique un evento del tipo que acabas de declarar:

    funciones/index.js

    // Import the Eventarc library:
    import { initializeApp } from "firebase-admin/app";
    import { getEventarc } from "firebase-admin/eventarc";
    
    const app = initializeApp();
    
    // In makeuppercase, after upperRef.set(uppercase), add:
    
    // Set eventChannel to a newly-initialized channel, or `undefined` if events
    // aren't enabled.
    const eventChannel =
      process.env.EVENTARC_CHANNEL &&
      getEventarc().channel(process.env.EVENTARC_CHANNEL, {
        allowedEventTypes: process.env.EXT_SELECTED_EVENTS,
      });
    
    // If events are enabled, publish a `complete` event to the configured
    // channel.
    eventChannel &&
      eventChannel.publish({
        type: "test-publisher.rtdb-uppercase-messages.v1.complete",
        subject: upperRef.toString(),
        data: {
          "original": original,
          "uppercase": uppercase,
        },
      });
    

    Este código de ejemplo aprovecha el hecho de que la variable de entorno EVENTARC_CHANNEL se define solo cuando el usuario habilitó al menos un tipo de evento. Si EVENTARC_CHANNEL no está definido, el código no intenta publicar ningún evento.

    Puede adjuntar información adicional a un evento de Eventarc. En el ejemplo anterior, el evento tiene un campo subject que contiene una referencia al valor recién creado y una carga data que contiene los mensajes originales y en mayúsculas. Las funciones definidas por el usuario que desencadenan el evento pueden hacer uso de esta información.

  3. Normalmente, las variables de entorno EVENTARC_CHANNEL y EXT_SELECTED_EVENTS se definen en función de las opciones que el usuario seleccionó durante la instalación. Para realizar pruebas con el emulador, defina manualmente estas variables en el archivo rtdb-uppercase-messages.env :

    EVENTARC_CHANNEL=locations/us-central1/channels/firebase
    EXT_SELECTED_EVENTS=test-publisher.rtdb-uppercase-messages.v1.complete
    

En este punto, ha completado los pasos necesarios para agregar un enlace de evento asincrónico a su extensión.

Para probar esta nueva característica que acaba de implementar, en los siguientes pasos, asuma el rol de un usuario que está instalando la extensión:

  1. Desde el directorio functions/integration-tests , inicializa un nuevo proyecto de Firebase:

    firebase init functions
    

    Cuando se le solicite, rechace configurar un proyecto predeterminado, seleccione JavaScript como lenguaje de Cloud Functions e instale las dependencias requeridas. Este proyecto representa el proyecto de un usuario que tiene su extensión instalada.

  2. Edite integration-tests/functions/index.js y pegue el siguiente código:

    import { logger } from "firebase-functions/v1";
    import { onCustomEventPublished } from "firebase-functions/v2/eventarc";
    
    import { initializeApp } from "firebase-admin/app";
    import { getDatabase } from "firebase-admin/database";
    
    const app = initializeApp();
    
    export const extraemphasis = onCustomEventPublished(
      "test-publisher.rtdb-uppercase-messages.v1.complete",
      async (event) => {
        logger.info("Received makeuppercase completed event", event);
    
        const refUrl = event.subject;
        const ref = getDatabase().refFromURL(refUrl);
        const upper = (await ref.get()).val();
        return ref.set(`${upper}!!!`);
      }
    );
    

    Este es un ejemplo de una función de posprocesamiento que un usuario podría escribir. En este caso, la función escucha la extensión para publicar un evento complete y, cuando se activa, agrega tres signos de exclamación al mensaje recién escrito en mayúsculas.

  3. Reinicia el emulador. El emulador cargará las funciones de la extensión, así como la función de posprocesamiento que definió el "usuario".

  4. Visite la interfaz de usuario del emulador de base de datos y edite el nodo raíz de la base de datos, utilizando la ruta que definió anteriormente:

    • Campo: msgs
    • Tipo: json
    • Valor: {"11": {"original": "recipe"}}

    Cuando guarda los cambios de su base de datos, la función makeuppercase de la extensión y la función extraemphasis del usuario deben activarse en secuencia, lo que da como resultado que el campo upper obtenga el valor RECIPE!!! .

8. Agregar controladores de eventos del ciclo de vida

La extensión que ha escrito hasta ahora procesa los mensajes a medida que se crean. Pero ¿qué pasa si tus usuarios ya tienen una base de datos de mensajes cuando instalan la extensión? Firebase Extensions tiene una función llamada enlaces de eventos de ciclo de vida que puede usar para activar acciones cuando su extensión se instala, actualiza o reconfigura. En esta sección, utilizará enlaces de eventos del ciclo de vida para rellenar la base de datos de mensajes existente de un proyecto con mensajes en mayúsculas cuando un usuario instale su extensión.

Firebase Extensions usa Cloud Tasks para ejecutar los controladores de eventos del ciclo de vida. Usted define controladores de eventos mediante Cloud Functions; Cada vez que una instancia de su extensión alcanza uno de los eventos del ciclo de vida admitidos, si ha definido un controlador, lo agregará a una cola de Cloud Tasks. Luego, Cloud Tasks ejecutará el controlador de forma asincrónica. Mientras se ejecuta un controlador de eventos del ciclo de vida, Firebase console informará al usuario que la instancia de extensión tiene una tarea de procesamiento en progreso. Depende de su función de controlador informar al usuario sobre el estado actual y la finalización de la tarea.

Para agregar un controlador de eventos de ciclo de vida que rellene los mensajes existentes, haga lo siguiente:

  1. Defina una nueva función en la nube que se active mediante eventos de la cola de tareas:

    funciones/index.js

    import { tasks } from "firebase-functions/v1";
    
    import { getDatabase } from "firebase-admin/database";
    import { getExtensions } from "firebase-admin/extensions";
    import { getFunctions } from "firebase-admin/functions";
    
    export const backfilldata = tasks.taskQueue().onDispatch(async () => {
      const batch = await getDatabase()
        .ref(process.env.MESSAGE_PATH)
        .parent.parent.orderByChild("upper")
        .limitToFirst(20)
        .get();
    
      const promises = [];
      for (const key in batch.val()) {
        const msg = batch.child(key);
        if (msg.hasChild("original") && !msg.hasChild("upper")) {
          const upper = msg.child("original").val().toUpperCase();
          promises.push(msg.child("upper").ref.set(upper));
        }
      }
      await Promise.all(promises);
    
      if (promises.length > 0) {
        const queue = getFunctions().taskQueue(
          "backfilldata",
          process.env.EXT_INSTANCE_ID
        );
        return queue.enqueue({});
      } else {
        return getExtensions()
          .runtime()
          .setProcessingState("PROCESSING_COMPLETE", "Backfill complete.");
      }
    });
    

    Tenga en cuenta que la función solo procesa unos pocos registros antes de volver a agregarse a la cola de tareas. Esta es una estrategia comúnmente utilizada para lidiar con tareas de procesamiento que no pueden completarse dentro del período de tiempo de espera de una función en la nube. Dado que no se puede predecir cuántos mensajes un usuario podría tener ya en su base de datos cuando instala su extensión, esta estrategia es una buena opción.

  2. En el archivo extension.yaml , declara tu función de reabastecimiento como un recurso de extensión que tiene la propiedad taskQueueTrigger :

    resources:
      - name: makeuppercase
        ...
      - name: backfilldata
        type: firebaseextensions.v1beta.function
        description: >-
          Backfill existing messages with uppercase versions
        properties:
          runtime: "nodejs18"
          taskQueueTrigger: {}
    

    Luego declare la función como controlador del evento del ciclo de vida onInstall :

    lifecycleEvents:
      onInstall:
        function: backfilldata
        processingMessage: Uppercasing existing messages
    
  3. Aunque es bueno rellenar los mensajes existentes, la extensión aún podría funcionar sin él. En situaciones como ésta, debería hacer que la ejecución de los controladores de eventos del ciclo de vida sea opcional.

    Para hacerlo, agregue un nuevo parámetro a extension.yaml :

    - param: DO_BACKFILL
      label: Backfill existing messages
      description: >-
        Generate uppercase versions of existing messages?
      type: select
      required: true
      options:
        - label: Yes
          value: true
        - label: No
          value: false
    

    Luego, al comienzo de la función de reabastecimiento, verifique el valor del parámetro DO_BACKFILL y salga temprano si no está configurado:

    funciones/index.js

    if (!process.env.DO_BACKFILL) {
      return getExtensions()
        .runtime()
        .setProcessingState("PROCESSING_COMPLETE", "Backfill skipped.");
    }
    

Con los cambios anteriores, la extensión ahora convertirá los mensajes existentes a mayúsculas cuando se instale.

Hasta este punto, utilizó el emulador de extensión para desarrollar su extensión y probar los cambios en curso. Sin embargo, el emulador de extensión omite el proceso de instalación, por lo que para probar su controlador de eventos onInstall , deberá instalar la extensión en un proyecto real. Pero eso es mejor, ya que con la adición de esta función de reposición automática, ¡la extensión del tutorial ahora tiene el código completo!

9. Implementar en un proyecto real de Firebase

Aunque el emulador de extensiones es una gran herramienta para iterar rápidamente una extensión durante el desarrollo, en algún momento querrás probarlo en un proyecto real.

Para hacerlo, primero configure un nuevo proyecto con algunos servicios habilitados:

  1. En Firebase console , agrega un nuevo proyecto.
  2. Actualice su proyecto al plan Blaze de pago por uso. Cloud Functions para Firebase requiere que su proyecto tenga una cuenta de facturación, por lo que también necesita una cuenta de facturación para instalar una extensión.
  3. En su nuevo proyecto, habilite la base de datos en tiempo real .
  4. Dado que desea probar la capacidad de su extensión para reponer los datos existentes durante la instalación, importe algunos datos de muestra a su instancia de base de datos en tiempo real:
    1. Descargue algunos datos iniciales de RTDB .
    2. En la página Base de datos en tiempo real de Firebase console, haga clic en (más) > Importar JSON y seleccione el archivo que acaba de descargar.
  5. Para habilitar la función de reposición para utilizar el método orderByChild , configure la base de datos para indexar mensajes según el valor de upper :

    {
      "rules": {
        ".read": false,
        ".write": false,
        "messages": {
          ".indexOn": "upper"
        }
      }
    }
    

Ahora instale su extensión desde la fuente local en el nuevo proyecto:

  1. Crea un nuevo directorio para tu proyecto de Firebase:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Inicialice un proyecto de Firebase en el directorio de trabajo:

    firebase init database
    

    Cuando se le solicite, seleccione el proyecto que acaba de crear.

  3. Instale la extensión en su proyecto local de Firebase:

    firebase ext:install /path/to/rtdb-uppercase-messages
    

    Aquí puede ver cómo es la experiencia del usuario al instalar una extensión utilizando la herramienta Firebase CLI. Asegúrese de seleccionar "sí" cuando la herramienta de configuración le pregunte si desea rellenar su base de datos existente.

    Después de seleccionar las opciones de configuración, Firebase CLI guardará su configuración en el directorio extensions y registrará la ubicación de origen de la extensión en el archivo firebase.json . En conjunto, estos dos registros se denominan manifiesto de extensiones . Los usuarios pueden usar el manifiesto para guardar la configuración de sus extensiones e implementarla en diferentes proyectos.

  4. Implemente la configuración de su extensión en su proyecto en vivo:

    firebase deploy --only extensions
    

Si todo va bien, Firebase CLI debería cargar su extensión en su proyecto e instalarla. Una vez completada la instalación, se ejecutará la tarea de reposición y, en unos minutos, su base de datos se actualizará con mensajes en mayúsculas. Agregue algunos nodos nuevos a la base de datos de mensajes y asegúrese de que la extensión también funcione para mensajes nuevos.

10. Escribir documentación

Antes de compartir su extensión con los usuarios, asegúrese de proporcionarles suficiente documentación para que tengan éxito.

Cuando inicializó el proyecto de extensión, Firebase CLI creó versiones auxiliares de la documentación mínima requerida. Actualice estos archivos para reflejar con precisión la extensión que ha creado.

extensión.yaml

Ya has estado actualizando este archivo a medida que desarrollaste esta extensión, por lo que no necesitas realizar más actualizaciones en este momento.

Sin embargo, no pase por alto la importancia de la documentación contenida en este archivo. Además de la información de identificación crucial de una extensión (nombre, descripción, autor, ubicación del repositorio oficial), el archivo extension.yaml contiene documentación accesible al usuario para cada recurso y parámetro configurable por el usuario. Esta información se muestra a los usuarios en Firebase console, Extensions Hub y Firebase CLI.

PREINSTALAR.md

En este archivo, proporcione la información que el usuario necesita antes de instalar su extensión: describa brevemente qué hace la extensión, explique los requisitos previos y brinde al usuario información sobre las implicaciones de facturación de instalar la extensión. Si tiene un sitio web con información adicional, este también es un buen lugar para vincularlo.

El texto de este archivo se muestra al usuario en Extensions Hub y mediante el comando firebase ext:info .

A continuación se muestra un ejemplo de un archivo PREINSTALL:

Use this extension to automatically convert strings to upper case when added to
a specified Realtime Database path.

This extension expects a database layout like the following example:

    "messages": {
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
      MESSAGE_ID: {
        "original": MESSAGE_TEXT
      },
    }

When you create new string records, this extension creates a new sibling record
with upper-cased text:

    MESSAGE_ID: {
      "original": MESSAGE_TEXT,
      "upper": UPPERCASE_MESSAGE_TEXT,
    }

#### Additional setup

Before installing this extension, make sure that you've
[set up Realtime Database](https://firebase.google.com/docs/database/quickstart)
in your Firebase project.

#### Billing

To install an extension, your project must be on the
[Blaze (pay as you go) plan](https://firebase.google.com/pricing).

- This extension uses other Firebase and Google Cloud Platform services, which
  have associated charges if you exceed the service's no-cost tier:
  - Realtime Database
  - Cloud Functions (Node.js 10+ runtime)
    [See FAQs](https://firebase.google.com/support/faq#extensions-pricing)
- If you enable events,
  [Eventarc fees apply](https://cloud.google.com/eventarc/pricing).

POSTINSTALL.md

Este archivo contiene información útil para los usuarios después de haber instalado correctamente su extensión: por ejemplo, pasos de configuración de seguimiento, un ejemplo de la extensión en acción, etc.

El contenido de POSTINSTALL.md se muestra en Firebase console después de configurar e instalar una extensión. Puede hacer referencia a los parámetros de usuario en este archivo y serán reemplazados por los valores configurados.

Aquí hay un archivo de ejemplo posterior a la instalación para la extensión del tutorial:

### See it in action

You can test out this extension right away!

1.  Go to your
    [Realtime Database dashboard](https://console.firebase.google.com/project/${param:PROJECT_ID}/database/${param:PROJECT_ID}/data) in the Firebase console.

1.  Add a message string to a path that matches the pattern `${param:MESSAGE_PATH}`.

1.  In a few seconds, you'll see a sibling node named `upper` that contains the
    message in upper case.

### Using the extension

We recommend adding data by pushing -- for example,
`firebase.database().ref().push()` -- because pushing assigns an automatically
generated ID to the node in the database. During retrieval, these nodes are
guaranteed to be ordered by the time they were added. Learn more about reading
and writing data for your platform (iOS, Android, or Web) in the
[Realtime Database documentation](https://firebase.google.com/docs/database/).

### Monitoring

As a best practice, you can
[monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor)
of your installed extension, including checks on its health, usage, and logs.

CAMBIOLOG.md

También debe documentar los cambios que realiza entre versiones de una extensión en el archivo CHANGELOG.md .

Dado que la extensión de ejemplo nunca antes se había publicado, el registro de cambios solo tiene una entrada:

## Version 0.0.1

Initial release of the _Convert messages to upper case_ extension.

LÉAME.md

La mayoría de las extensiones también proporcionan un archivo Léame para beneficio de los usuarios que visitan el repositorio de la extensión. Puedes escribir este archivo a mano o generar un Léame usando el comando.

A los efectos de esta guía, omita la escritura de un archivo Léame.

Documentación adicional

La documentación comentada anteriormente es el conjunto mínimo de documentación que debe proporcionar a los usuarios. Muchas extensiones requieren documentación más detallada para que los usuarios las utilicen correctamente. Cuando este sea el caso, debe escribir documentación adicional y alojarla en algún lugar al que pueda dirigir a los usuarios.

A los efectos de esta guía, omita la redacción de documentación más extensa.

11. Publicar en Extensions Hub

Ahora que su extensión tiene el código completo y está documentada, está listo para compartirla con el mundo en Extensions Hub. Pero como esto es sólo un tutorial, no lo hagas. Vaya y comience a escribir su propia extensión utilizando lo que ha aprendido aquí y en el resto de la documentación del editor de Firebase Extensions, y examinando la fuente de las extensiones oficiales escritas por Firebase.

Cuando esté listo para publicar su trabajo en Extensions Hub, así es como lo hará:

  1. Si está publicando su primera extensión, regístrese como editor de extensiones . Cuando se registra como editor de extensiones, crea una ID de editor que permite a los usuarios identificarlo rápidamente como el autor de sus extensiones.
  2. Aloje el código fuente de su extensión en una ubicación públicamente verificable. Cuando su código esté disponible en una fuente verificable, Firebase puede publicar su extensión directamente desde esta ubicación. Hacerlo ayuda a garantizar que está publicando la versión actualmente publicada de su extensión y ayuda a los usuarios permitiéndoles examinar el código que están instalando en sus proyectos.

    Actualmente, esto significa que su extensión esté disponible en un repositorio público de GitHub.

  3. Cargue su extensión en Extensions Hub usando el comando firebase ext:dev:upload .

  4. Vaya al panel de su editor en Firebase console, busque la extensión que acaba de cargar y haga clic en "Publicar en Extensions Hub". Esto solicita una revisión por parte de nuestro personal de revisión, lo que puede demorar algunos días. Si se aprueba, la extensión se publicará en Extensions Hub. Si es rechazado, recibirá un mensaje explicando el motivo; Luego puede abordar los problemas informados y volver a enviarlos para su revisión.