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.
Instale Node.js 16 o posterior. Una forma de instalar Node es mediante nvm (o nvm-windows ).
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:
Cree un directorio para su extensión y
cd
en él:mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
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.
Utilice firebase ext:dev:init
para inicializar un nuevo directorio de 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:
Cambie al directorio
integration-tests
:cd functions/integration-tests
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.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".
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); });
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.
El uso del emulador de extensiones puede acelerar el desarrollo al permitirle probar e iterar rápidamente su código.
Más información
Obtenga más información sobre el uso del emulador de extensiones .
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
.
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.Dado que cambió el nombre de su extensión, también debe actualizar la configuración de su emulador con el nuevo nombre:
- En
functions/integration-tests/firebase.json
, cambiegreet-the-world
artdb-uppercase-messages
. - Cambie el nombre
functions/integration-tests/extensions/greet-the-world.env
afunctions/integration-tests/extensions/rtdb-uppercase-messages.env
.
- En
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.
El archivo
extension.yaml
contiene metadatos sobre su extensión. El más básico de estos metadatos es el nombre de su extensión y una descripción de lo que hace.Asigne un nombre a sus extensiones con el siguiente formato:
<firebase-product>-<description-of-tasks-performed>
.
Más información
La referencia extension.yaml
tiene una especificación completa del archivo; sin embargo, esta documentación analizará los usos específicos de este archivo según sea necesario.
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.
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
yexport
) en lugar de CommonJS (require
). Para usar módulos ES en Node, especifique"type": "module"
enfunctions/package.json
:{ "name": "rtdb-uppercase-messages", "main": "index.js", "type": "module", … }
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ó conmakeuppercase
, también necesita actualizar su declaración.Abra
extension.yaml
y agregue un camporesources
: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"
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:
Si el emulador aún se está ejecutando, deténgalo presionando Ctrl-C.
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.
Reinicia el emulador:
firebase emulators:start --project=demo-test
Pruebe su extensión actualizada:
Abra la interfaz de usuario del emulador de base de datos utilizando el enlace que el emulador imprimió cuando lo inició.
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.- Campo:
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 campouppercase
que contenga el contenido en mayúscula del campooriginal
.
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.
- Las funciones que componen la lógica de su extensión deben definirse como código de Cloud Functions y declararse como un recurso de extensión en el archivo
extension.yaml
. - Puedes escribir funciones que se activen cuando se accede a puntos finales HTTP o en respuesta a eventos emitidos por productos Firebase, productos Google Cloud y otras extensiones.
Más información
- Obtenga más información sobre cómo escribir Cloud Functions para extensiones , incluido más información sobre los activadores de eventos admitidos.
- La referencia
extension.yaml
tiene una especificación completa del archivo; sin embargo, esta documentación analizará los usos específicos de este archivo según sea necesario. - La documentación de Cloud Functions para Firebase tiene información general sobre el uso de Cloud Functions, no específica de Firebase Extensions.
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.
- Declare cualquier función de IAM que su extensión necesite en el campo
roles
deextensions.yaml
. Cuando se instalan, a las extensiones se les otorgan automáticamente estos roles. - Declare cualquier API de Google que su extensión necesite en el campo
apis
deextensions.yaml
. Cuando los usuarios instalan su extensión, pueden optar por habilitar automáticamente estas API para su proyecto. - Para fines de documentación, declara cualquier API que no sea de Google que tu extensión necesite en el campo
externalServices
deextensions.yaml
.
Más información
- Obtenga más información sobre cómo configurar el acceso adecuado para una extensión .
- La referencia
extension.yaml
tiene una especificación completa del archivo; sin embargo, esta documentación analizará los usos específicos de este archivo según sea necesario.
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:
En el archivo
extension.yaml
, agregue una secciónparams
:- 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.
Aún en el archivo
extension.yaml
, regrese a su declaraciónmakeuppercase
y cambie el camporesource
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ónmakeuppercase
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 deextension.yaml
(y enPOSTINSTALL.md
; hablaremos de esto más adelante).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.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. ...
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ónGREETING
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.
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.- Campo:
- Puede ofrecer a los usuarios la posibilidad de personalizar su extensión según sus necesidades declarando parámetros definidos por el usuario en el archivo
extension.yaml
. A los usuarios se les solicita que definan estos valores cuando instalan su extensión. - Puede hacer referencia a valores de parámetros definidos por el usuario dentro del archivo
extension.yaml
y en el archivoPOSTINSTALL.md
usando la siguiente sintaxis:${param:PARAMETER_NAME}
- Puede acceder a valores de parámetros definidos por el usuario dentro de su código de Cloud Functions como variables de entorno:
process.env.PARAMETER_NAME
- Al realizar pruebas con el emulador, defina los parámetros de usuario en el archivo
<extension-name>.env
.
Más información
Obtenga más información sobre cómo configurar y usar parámetros en su extensión .
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:
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 usatest-publisher
por ahora).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. SiEVENTARC_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 cargadata
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.Normalmente, las variables de entorno
EVENTARC_CHANNEL
yEXT_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 archivortdb-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:
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.
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.Reinicia el emulador. El emulador cargará las funciones de la extensión, así como la función de posprocesamiento que definió el "usuario".
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ónextraemphasis
del usuario deben activarse en secuencia, lo que da como resultado que el campoupper
obtenga el valorRECIPE!!!
.- Campo:
- Sus extensiones pueden incluir enlaces que permitan a los usuarios insertar su propia lógica en la operación básica de su extensión.
- Los enlaces de usuario pueden ser sincrónicos, lo que bloquea la ejecución de una extensión hasta que se completa. Las extensiones suelen utilizar enlaces sincrónicos para realizar tareas de preprocesamiento definidas por el usuario.
- Los enlaces de usuario también pueden ser asíncronos, como en el ejemplo anterior. Se pueden utilizar enlaces asincrónicos para ejecutar lógica definida por el usuario que no es crítica para que la extensión funcione correctamente.
Más información
Obtenga más información sobre cómo agregar enlaces para la lógica definida por el usuario , incluidos enlaces asíncronos y síncronos.
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:
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.
En el archivo
extension.yaml
, declara tu función de reabastecimiento como un recurso de extensión que tiene la propiedadtaskQueueTrigger
: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
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!
Los eventos del ciclo de vida se activan cuando los usuarios realizan determinadas tareas de administración de extensiones:
- Instalación de una instancia de una extensión
- Actualizar una instancia de una extensión a una nueva versión
- Reconfigurar una instancia de una extensión
Puede definir funciones que se activen en los eventos del ciclo de vida de su extensión.
Utilice la API de tiempo de ejecución de extensión del SDK de administración para informar al usuario el estado de un controlador de eventos del ciclo de vida. Los usuarios verán el estado de procesamiento actual de una extensión en Firebase console.
Las funciones que operan en toda su base de datos (como las operaciones de reabastecimiento) a menudo no pueden completarse antes de que expire el tiempo de espera de la función de nube. Puede evitar este problema dividiendo su tarea en varias invocaciones de funciones.
Si su extensión incluye controladores de eventos del ciclo de vida que no son críticos para que la extensión funcione, debe hacer que la ejecución del controlador sea configurable por el usuario.
Más información
Obtenga más información sobre cómo manejar los eventos del ciclo de vida de su extensión .
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:
- En Firebase console , agrega un nuevo proyecto.
- 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.
- En su nuevo proyecto, habilite la base de datos en tiempo real .
- 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:
- Descargue algunos datos iniciales de RTDB .
- 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.
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 deupper
:{ "rules": { ".read": false, ".write": false, "messages": { ".indexOn": "upper" } } }
Ahora instale su extensión desde la fuente local en el nuevo proyecto:
Crea un nuevo directorio para tu proyecto de Firebase:
mkdir ~/extensions-live-test && cd ~/extensions-live-test
Inicialice un proyecto de Firebase en el directorio de trabajo:
firebase init database
Cuando se le solicite, seleccione el proyecto que acaba de crear.
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 archivofirebase.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.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.
- Los usuarios pueden crear un manifiesto de extensión usando el comando
firebase ext:install
. También puede utilizar este comando para instalar una extensión desde una fuente local. - Implemente una configuración de extensión desde un manifiesto en un proyecto en vivo usando
firebase deploy
. - Aunque no se muestra aquí, los usuarios también pueden instalar extensiones en sus proyectos desde Extensions Hub.
Más información
Consulte la documentación del usuario sobre cómo administrar configuraciones de proyectos con el manifiesto de Extensiones .
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.
- Como mínimo, cada extensión debe proporcionar documentación de usuario en los siguientes archivos:
extension.yaml
,PREINSTALL.md
,POSTINSTALL.md
yCHANGELOG.md
. - También debe proporcionar a los usuarios documentación más detallada cuando sea necesario.
Más información
Consulte la documentación sobre redacción de documentación .
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á:
- 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.
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.
Cargue su extensión en Extensions Hub usando el comando
firebase ext:dev:upload
.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.
- Para compartir extensiones en Extensions Hub, debe estar registrado como editor.
- Se requiere la publicación desde una fuente verificable y les brinda a los usuarios la seguridad de que el código que están instalando es el mismo código que pueden examinar en GitHub.
- Utilice el comando
firebase ext:dev:upload
para cargar una extensión en Extensions Hub. - Envíe sus extensiones para su revisión desde el panel del editor.
Más información
Obtenga más información sobre cómo registrarse como editor y publicar una extensión .