Esta página orienta você nas etapas necessárias para criar uma extensão simples do Firebase, que você pode instalar em seus projetos ou compartilhar com outras pessoas. Este exemplo simples de extensão do Firebase observará mensagens no Realtime Database e as converterá em letras maiúsculas.
1. Configure seu ambiente e inicialize um projeto
Antes de começar a criar uma extensão, você precisará configurar um ambiente de compilação com as ferramentas necessárias.
Instale o Node.js 16 ou mais recente. Uma maneira de instalar o Node é usando nvm (ou nvm-windows ).
Instale ou atualize para a versão mais recente da CLI do Firebase . Para instalar ou atualizar usando
npm
, execute este comando:npm install -g firebase-tools
Agora use a CLI do Firebase para inicializar um novo projeto de extensão:
Crie um diretório para sua extensão e
cd
nele:mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
Execute o comando
ext:dev:init
da CLI do Firebase:firebase ext:dev:init
Quando solicitado, escolha JavaScript como linguagem para funções (mas observe que você também pode usar TypeScript ao desenvolver sua própria extensão) e, quando solicitado a instalar dependências, responda "sim". (Aceite os padrões para quaisquer outras opções.) Este comando configurará um esqueleto de base de código para uma nova extensão, a partir da qual você poderá começar a desenvolver sua extensão.
Use firebase ext:dev:init
para inicializar um novo diretório de extensão.
2. Experimente a extensão de exemplo usando o emulador
Quando a CLI do Firebase inicializou o novo diretório de extensões, ela criou uma função de exemplo simples e um diretório de integration-tests
que contém os arquivos necessários para executar uma extensão usando o conjunto de emuladores do Firebase.
Tente executar a extensão de exemplo no emulador:
Mude para o diretório
integration-tests
:cd functions/integration-tests
Inicie o emulador com um projeto de demonstração:
firebase emulators:start --project=demo-test
O emulador carrega a extensão em um projeto "fictício" predefinido (
demo-test
). A extensão até agora consiste em uma única função acionada por HTTP,greetTheWorld
, que retorna uma mensagem “olá mundo” quando acessada.Com o emulador ainda em execução, experimente a função
greetTheWorld
da extensão visitando o URL impresso quando você o iniciou.Seu navegador exibe a mensagem "Hello World from greet-the-world".
O código-fonte desta função está no diretório
functions
da extensão. Abra o código-fonte no editor ou IDE de sua preferência:funções/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); });
Enquanto o emulador estiver em execução, ele recarregará automaticamente todas as alterações feitas no código do Functions. Tente fazer uma pequena alteração na função
greetTheWorld
:funções/index.js
const greeting = `${consumerProvidedGreeting} everyone, from ${instanceId}`;
Salve suas alterações. O emulador recarregará seu código e agora, ao visitar o URL da função, você verá a saudação atualizada.
Usar o emulador de extensões pode acelerar o desenvolvimento, permitindo testar e iterar rapidamente em seu código.
Mais Informações
Saiba mais sobre como usar o emulador de extensões .
3. Adicione informações básicas a extension.yaml
Agora que você tem um ambiente de desenvolvimento configurado e está executando o emulador de extensões, você pode começar a escrever sua própria extensão.
Como um primeiro passo modesto, edite os metadados da extensão predefinidos para refletir a extensão que você deseja escrever em vez de greet-the-world
. Esses metadados são armazenados no arquivo extension.yaml
.
Abra
extension.yaml
em seu editor e substitua todo o conteúdo do arquivo pelo seguinte: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
Observe a convenção de nomenclatura usada no campo
name
: as extensões oficiais do Firebase são nomeadas com um prefixo que indica o produto principal do Firebase no qual a extensão opera, seguido por uma descrição do que a extensão faz. Você deve usar a mesma convenção em suas próprias extensões.Como você alterou o nome da sua extensão, você também deve atualizar a configuração do seu emulador com o novo nome:
- Em
functions/integration-tests/firebase.json
, alteregreet-the-world
parartdb-uppercase-messages
. - Renomeie
functions/integration-tests/extensions/greet-the-world.env
parafunctions/integration-tests/extensions/rtdb-uppercase-messages.env
.
- Em
Ainda existem alguns resquícios da extensão greet-the-world
em seu código de extensão, mas deixe-os por enquanto. Você os atualizará nas próximas seções.
O arquivo
extension.yaml
contém metadados sobre sua extensão. O mais básico desses metadados é o nome da sua extensão e uma descrição do que ela faz.Nomeie suas extensões com o seguinte formato:
<firebase-product>-<description-of-tasks-performed>
.
Mais Informações
A referência extension.yaml
possui uma especificação completa do arquivo; no entanto, esta documentação discutirá usos específicos deste arquivo conforme necessário.
4. Escreva uma Cloud Function e declare-a como um recurso de extensão
Agora você pode começar a escrever algum código. Nesta etapa, você escreverá uma função do Cloud que executa a tarefa principal da sua extensão, que é observar mensagens no Realtime Database e convertê-las para letras maiúsculas.
Abra o código-fonte das funções da extensão (no diretório de
functions
da extensão) no editor ou IDE de sua escolha. Substitua seu conteúdo pelo seguinte:funções/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); });
A função antiga, que você substituiu, era uma função acionada por HTTP, executada quando um terminal HTTP era acessado. A nova função é acionada por eventos de banco de dados em tempo real: ela observa novos itens em um caminho específico e, quando um é detectado, grava a versão maiúscula do valor de volta no banco de dados.
A propósito, este novo arquivo usa a sintaxe do módulo ECMAScript (
import
eexport
) em vez de CommonJS (require
). Para usar módulos ES no Node, especifique"type": "module"
emfunctions/package.json
:{ "name": "rtdb-uppercase-messages", "main": "index.js", "type": "module", … }
Cada função em sua extensão deve ser declarada no arquivo
extension.yaml
. A extensão de exemplo declarougreetTheWorld
como a única Cloud Function da extensão; agora que você o substituiu pormakeuppercase
, você também precisa atualizar sua declaração.Abra
extension.yaml
e adicione um 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"
Como sua extensão agora usa o Realtime Database como acionador, você precisa atualizar a configuração do emulador para executar o emulador RTDB junto com o emulador do Cloud Functions:
Se o emulador ainda estiver em execução, pare-o pressionando Ctrl-C.
No diretório
functions/integration-tests
, execute o seguinte comando:firebase init emulators
Quando solicitado, pule a configuração de um projeto padrão e selecione os emuladores de funções e banco de dados. Aceite as portas padrão e permita que a ferramenta de configuração baixe todos os arquivos necessários.
Reinicie o emulador:
firebase emulators:start --project=demo-test
Experimente sua extensão atualizada:
Abra a UI do emulador de banco de dados usando o link que o emulador imprimiu quando você o iniciou.
Edite o nó raiz do banco de dados:
- Campo:
messages
- Tipo:
json
- Valor:
{"11": {"original": "recipe"}}
Se tudo estiver configurado corretamente, ao salvar as alterações no banco de dados, a função da extensão
makeuppercase
deverá acionar e adicionar um registro filho à mensagem 11 com o conteúdo"upper": "RECIPE"
. Dê uma olhada nos logs e nas guias do banco de dados da IU do emulador para confirmar os resultados esperados.- Campo:
Tente adicionar mais alguns filhos ao nó
messages
({"original":"any text"}
). Sempre que você adicionar um novo registro, a extensão deverá adicionar um campouppercase
contendo o conteúdo maiúsculo do campooriginal
.
Agora você tem uma extensão completa, embora simples, que opera em uma instância RTDB. Nas seções a seguir, você refinará esta extensão com alguns recursos adicionais. Em seguida, você preparará a extensão para distribuição a outras pessoas e, por fim, aprenderá como publicar sua extensão no Extensions Hub.
- As funções que compõem a lógica da sua extensão devem ser definidas como código do Cloud Functions e declaradas como um recurso de extensão no arquivo
extension.yaml
. - Você pode escrever funções que são acionadas quando endpoints HTTP são acessados ou em resposta a eventos emitidos por produtos Firebase, produtos Google Cloud e outras extensões.
Mais Informações
- Saiba mais sobre como escrever Cloud Functions para extensões , incluindo mais sobre os acionadores de eventos compatíveis.
- A referência
extension.yaml
possui uma especificação completa do arquivo; no entanto, esta documentação discutirá usos específicos deste arquivo conforme necessário. - A documentação do Cloud Functions para Firebase contém informações gerais sobre o uso do Cloud Functions, não específicas das extensões do Firebase.
5. Declare APIs e funções
O Firebase concede a cada instância de uma extensão instalada acesso limitado ao projeto e aos dados dele usando uma conta de serviço por instância. Cada conta possui o conjunto mínimo de permissões necessárias para operar. Por esse motivo, você deve declarar explicitamente quaisquer funções do IAM exigidas pela sua extensão; quando os usuários instalam sua extensão, o Firebase cria uma conta de serviço com essas funções concedidas e a usa para executar a extensão.
Você não precisa declarar funções para acionar os eventos de um produto, mas precisa declarar uma função para interagir com ele de outra forma. Como a função adicionada na última etapa grava no Realtime Database, você precisa adicionar a seguinte declaração a extension.yaml
:
roles:
- role: firebasedatabase.admin
reason: Allows the extension to write to RTDB.
Da mesma forma, você declara as APIs do Google que uma extensão usa no campo apis
. Quando os usuários instalarem sua extensão, eles serão perguntados se desejam habilitar automaticamente essas APIs para seus projetos. Normalmente, isso só é necessário para APIs do Google que não são do Firebase e não é necessário para este guia.
- Declare qualquer função do IAM que sua extensão precise no campo de
roles
deextensions.yaml
. Quando instaladas, as extensões recebem automaticamente essas funções. - Declare todas as APIs do Google que sua extensão precisa no campo
apis
deextensions.yaml
. Quando os usuários instalam sua extensão, eles podem optar por ativar automaticamente essas APIs para seus projetos. - Para fins de documentação, declare quaisquer APIs que não sejam do Google que sua extensão precise no campo
externalServices
deextensions.yaml
.
Mais Informações
- Saiba mais sobre como configurar o acesso apropriado para uma extensão .
- A referência
extension.yaml
possui uma especificação completa do arquivo; no entanto, esta documentação discutirá usos específicos deste arquivo conforme necessário.
6. Defina parâmetros configuráveis pelo usuário
A função que você criou nas duas últimas etapas observou um local RTDB específico para mensagens recebidas. Às vezes, observar um local específico é realmente o que você deseja, como quando sua extensão opera em uma estrutura de banco de dados usada exclusivamente para sua extensão. No entanto, na maioria das vezes, você desejará tornar esses valores configuráveis pelos usuários que instalam sua extensão em seus projetos. Dessa forma, os usuários podem usar sua extensão para trabalhar com a configuração do banco de dados existente.
Torne o caminho que a extensão monitora para novas mensagens configurável pelo usuário:
No arquivo
extension.yaml
, adicione uma seçãoparams
:- 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
Isso define um novo parâmetro de string que os usuários serão solicitados a definir quando instalarem sua extensão.
Ainda no arquivo
extension.yaml
, volte para sua declaraçãomakeuppercase
e altere o camporesource
para o seguinte:resource: projects/_/instances/${DATABASE_INSTANCE}/refs/${param:MESSAGE_PATH}
O token
${param:MESSAGE_PATH}
é uma referência ao parâmetro que você acabou de definir. Quando sua extensão for executada, esse token será substituído por qualquer valor que o usuário configurou para esse parâmetro, fazendo com que a funçãomakeuppercase
escutará o caminho especificado pelo usuário. Você pode usar esta sintaxe para fazer referência a qualquer parâmetro definido pelo usuário em qualquer lugar emextension.yaml
(e emPOSTINSTALL.md
—mais sobre isso mais tarde).Você também pode acessar parâmetros definidos pelo usuário a partir do seu código de funções.
Na função que você escreveu na última seção, você codificou o caminho para observar as alterações. Altere a definição do gatilho para fazer referência ao valor definido pelo usuário:
funções/index.js
export const makeuppercase = database.ref(process.env.MESSAGE_PATH).onCreate
Observe que nas extensões do Firebase, essa alteração é puramente para fins de documentação: quando uma função do Cloud é implantada como parte de uma extensão, ela usa a definição do gatilho do arquivo
extension.yaml
e ignora o valor especificado na definição da função. No entanto, é uma boa ideia documentar em seu código a origem desse valor.Você pode achar decepcionante fazer uma alteração no código que não tenha efeito no tempo de execução, mas a lição importante a ser aprendida é que você pode acessar qualquer parâmetro definido pelo usuário em seu código de função e usá-lo como um valor comum na lógica da função. Como uma referência a esse recurso, adicione a seguinte instrução de log para demonstrar que você está realmente acessando o valor definido pelo usuário:
funções/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, os usuários são solicitados a fornecer valores para parâmetros quando instalam uma extensão. No entanto, ao usar o emulador para teste e desenvolvimento, você ignora o processo de instalação e, em vez disso, fornece valores para parâmetros definidos pelo usuário usando um arquivo
env
.Abra
functions/integration-tests/extensions/rtdb-uppercase-messages.env
e substitua a definiçãoGREETING
pelo seguinte:MESSAGE_PATH=/msgs/{pushId}/original
Observe que o caminho acima é diferente do caminho padrão e do caminho que você definiu anteriormente; isso é apenas para provar a si mesmo, quando você tenta sua extensão atualizada, que sua definição está entrando em vigor.
Agora, reinicie o emulador e visite novamente a IU do emulador de banco de dados.
Edite o nó raiz do banco de dados, usando o caminho definido acima:
- Campo:
msgs
- Tipo:
json
- Valor:
{"11": {"original": "recipe"}}
Quando você salva as alterações no banco de dados, a função
makeuppercase
da extensão deve ser acionada como antes, mas agora também deve imprimir o parâmetro definido pelo usuário no log do console.- Campo:
- Você pode dar aos usuários a capacidade de personalizar sua extensão de acordo com suas necessidades, declarando parâmetros definidos pelo usuário no arquivo
extension.yaml
. Os usuários são solicitados a definir esses valores quando instalam sua extensão. - Você pode fazer referência a valores de parâmetros definidos pelo usuário no arquivo
extension.yaml
e no arquivoPOSTINSTALL.md
usando a seguinte sintaxe:${param:PARAMETER_NAME}
- Você pode acessar valores de parâmetros definidos pelo usuário no código do Cloud Functions como variáveis de ambiente:
process.env.PARAMETER_NAME
- Ao testar usando o emulador, defina os parâmetros do usuário no arquivo
<extension-name>.env
.
Mais Informações
Saiba mais sobre como configurar e usar parâmetros na sua extensão .
7. Fornece ganchos de eventos para lógica definida pelo usuário
Você já viu, como autor da extensão, como um produto do Firebase pode acionar a lógica fornecida pela extensão: a criação de novos registros no Realtime Database aciona a função makeuppercase
. Sua extensão pode ter um relacionamento análogo com os usuários que a instalam: sua extensão pode acionar uma lógica definida pelo usuário .
Uma extensão pode fornecer ganchos síncronos , ganchos assíncronos ou ambos. Os ganchos síncronos oferecem aos usuários uma maneira de executar tarefas que bloqueiam a conclusão de uma das funções da extensão. Isso pode ser útil, por exemplo, para fornecer aos usuários uma maneira de realizar o pré-processamento personalizado antes que uma extensão faça seu trabalho.
Neste guia, você adicionará um gancho assíncrono à sua extensão, o que permitirá que os usuários definam suas próprias etapas de processamento a serem executadas depois que a extensão gravar a mensagem em maiúscula no Realtime Database. Os ganchos assíncronos usam o Eventarc para acionar funções definidas pelo usuário. As extensões declaram os tipos de eventos que emitem e, quando os usuários instalam a extensão, eles escolhem em quais tipos de eventos estão interessados. Se escolherem pelo menos um evento, o Firebase provisionará um canal Eventarc para a extensão como parte do processo de instalação . Os usuários podem então implantar suas próprias funções de nuvem que escutam nesse canal e são acionadas quando a extensão publica novos eventos.
Siga estas etapas para adicionar um gancho assíncrono:
No arquivo
extension.yaml
, adicione a seção a seguir, que declara o tipo de evento que a extensão emite: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.
Os tipos de eventos devem ser universalmente exclusivos; para garantir a exclusividade, sempre nomeie seus eventos usando o seguinte formato:
<publisher-id>.<extension-id>.<version>.<description>
. (Você ainda não tem um ID de editor, então usetest-publisher
por enquanto.)No final da função
makeuppercase
, adicione algum código que publique um evento do tipo que você acabou de declarar:funções/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 exemplo aproveita o fato de que a variável de ambiente
EVENTARC_CHANNEL
é definida somente quando o usuário ativou pelo menos um tipo de evento. seEVENTARC_CHANNEL
não estiver definido, o código não tentará publicar nenhum evento.Você pode anexar informações extras a um evento Eventarc. No exemplo acima, o evento tem um campo
subject
que contém uma referência ao valor recém-criado e uma carga dedata
que contém as mensagens originais e em maiúsculas. Funções definidas pelo usuário que acionam o evento podem fazer uso dessas informações.Normalmente, as variáveis de ambiente
EVENTARC_CHANNEL
eEXT_SELECTED_EVENTS
são definidas com base nas opções selecionadas pelo usuário durante a instalação. Para testar com o emulador, defina manualmente estas variáveis no arquivortdb-uppercase-messages.env
:EVENTARC_CHANNEL=locations/us-central1/channels/firebase EXT_SELECTED_EVENTS=test-publisher.rtdb-uppercase-messages.v1.complete
Neste ponto, você concluiu as etapas necessárias para adicionar um gancho de evento assíncrono à sua extensão.
Para experimentar esse novo recurso que você acabou de implementar, nas próximas etapas, assuma o papel de um usuário que está instalando a extensão:
No diretório
functions/integration-tests
, inicialize um novo projeto do Firebase:firebase init functions
Quando solicitado, recuse a configuração de um projeto padrão, selecione JavaScript como linguagem do Cloud Functions e instale as dependências necessárias. Este projeto representa o projeto de um usuário , que possui sua extensão instalada.
Edite
integration-tests/functions/index.js
e cole o seguinte 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 é um exemplo de função de pós-processamento que um usuário pode escrever. Nesse caso, a função escuta a extensão para publicar um evento
complete
e, quando acionada, adiciona três pontos de exclamação à mensagem recém-maiúscula.Reinicie o emulador. O emulador carregará as funções da extensão, bem como a função de pós-processamento definida pelo "usuário".
Visite a UI do emulador de banco de dados e edite o nó raiz do banco de dados, usando o caminho definido acima:
- Campo:
msgs
- Tipo:
json
- Valor:
{"11": {"original": "recipe"}}
Quando você salva as alterações no banco de dados, a função
makeuppercase
da extensão e a funçãoextraemphasis
do usuário devem ser acionadas em sequência, resultando no campoupper
recebendo o valorRECIPE!!!
.- Campo:
- Suas extensões podem incluir ganchos que permitem aos usuários inserir sua própria lógica na operação básica da sua extensão.
- Os ganchos do usuário podem ser síncronos, o que bloqueia a execução de uma extensão até que seja concluída. As extensões geralmente usam ganchos síncronos para executar tarefas de pré-processamento definidas pelo usuário.
- Os ganchos do usuário também podem ser assíncronos, como no exemplo acima. Ganchos assíncronos podem ser usados para executar lógica definida pelo usuário que não é crítica para o funcionamento correto da extensão.
Mais Informações
Saiba mais sobre como adicionar ganchos para lógica definida pelo usuário , incluindo ganchos assíncronos e síncronos.
8. Adicione manipuladores de eventos de ciclo de vida
A extensão que você escreveu até agora processa as mensagens à medida que são criadas. Mas e se seus usuários já tiverem um banco de dados de mensagens quando instalarem a extensão? As Extensões do Firebase têm um recurso chamado ganchos de eventos de ciclo de vida que você pode usar para acionar ações quando sua extensão é instalada, atualizada ou reconfigurada. Nesta seção, você usará ganchos de eventos de ciclo de vida para preencher o banco de dados de mensagens existente de um projeto com mensagens em maiúsculas quando um usuário instalar sua extensão.
As extensões do Firebase usam o Cloud Tasks para executar os manipuladores de eventos do ciclo de vida. Você define manipuladores de eventos usando Cloud Functions; sempre que uma instância da sua extensão atingir um dos eventos de ciclo de vida suportados, se você tiver definido um manipulador, ele adicionará o manipulador a uma fila do Cloud Tasks. O Cloud Tasks executará o manipulador de forma assíncrona. Enquanto um manipulador de eventos de ciclo de vida estiver em execução, o console do Firebase informará ao usuário que a instância da extensão tem uma tarefa de processamento em andamento. Cabe à sua função de manipulador relatar o status contínuo e a conclusão da tarefa ao usuário.
Para adicionar um manipulador de eventos de ciclo de vida que preenche mensagens existentes, faça o seguinte:
Defina uma nova função do Cloud que seja acionada por eventos da fila de tarefas:
funções/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."); } });
Observe que a função processa apenas alguns registros antes de ser adicionada novamente à fila de tarefas. Essa é uma estratégia comumente usada para lidar com tarefas de processamento que não podem ser concluídas dentro da janela de tempo limite de uma função do Cloud. Como você não pode prever quantas mensagens um usuário já terá em seu banco de dados quando instalar sua extensão, essa estratégia é uma boa opção.
No arquivo
extension.yaml
, declare sua função de preenchimento como um recurso de extensão que possui a propriedadetaskQueueTrigger
:resources: - name: makeuppercase ... - name: backfilldata type: firebaseextensions.v1beta.function description: >- Backfill existing messages with uppercase versions properties: runtime: "nodejs18" taskQueueTrigger: {}
Em seguida, declare a função como o manipulador do evento de ciclo de vida
onInstall
:lifecycleEvents: onInstall: function: backfilldata processingMessage: Uppercasing existing messages
Embora seja bom preencher mensagens existentes, a extensão ainda pode funcionar sem ele. Em situações como essa, você deve tornar opcional a execução dos manipuladores de eventos do ciclo de vida.
Para fazer isso, adicione um novo 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
Então, no início da função backfill, verifique o valor do parâmetro
DO_BACKFILL
e saia mais cedo se não estiver definido:funções/index.js
if (!process.env.DO_BACKFILL) { return getExtensions() .runtime() .setProcessingState("PROCESSING_COMPLETE", "Backfill skipped."); }
Com as alterações acima, a extensão agora converterá as mensagens existentes em letras maiúsculas quando for instalada.
Até este ponto, você usou o emulador de extensão para desenvolver sua extensão e testar alterações contínuas. No entanto, o emulador de extensão ignora o processo de instalação, portanto, para testar seu manipulador de eventos onInstall
, você precisará instalar a extensão em um projeto real. Ainda bem que, com a adição desse recurso de preenchimento automático, a extensão do tutorial agora está com o código completo!
Os eventos de ciclo de vida são acionados quando os usuários realizam determinadas tarefas de gerenciamento de extensões:
- Instalando uma instância de uma extensão
- Atualizando uma instância de uma extensão para uma nova versão
- Reconfigurando uma instância de uma extensão
Você pode definir funções que são acionadas nos eventos do ciclo de vida da sua extensão.
Use a API de tempo de execução de extensão do SDK Admin para relatar o status de um manipulador de eventos de ciclo de vida ao usuário. Os usuários verão o status de processamento atual de uma extensão no console do Firebase.
As funções que operam em todo o banco de dados (como operações de preenchimento) geralmente não podem ser concluídas antes que o tempo limite do Cloud Function expire. Você pode evitar esse problema dividindo sua tarefa em diversas invocações de função.
Se sua extensão incluir manipuladores de eventos de ciclo de vida que não são críticos para o funcionamento da extensão, você deverá tornar a execução do manipulador configurável pelo usuário.
Mais Informações
Saiba mais sobre como lidar com os eventos do ciclo de vida da sua extensão .
9. Implante em um projeto real do Firebase
Embora o emulador de extensões seja uma ótima ferramenta para iterar rapidamente uma extensão durante o desenvolvimento, em algum momento você desejará experimentá-lo em um projeto real.
Para fazer isso, primeiro configure um novo projeto com alguns serviços habilitados:
- No console do Firebase , adicione um novo projeto.
- Atualize seu projeto para o plano Blaze pré-pago. O Cloud Functions para Firebase exige que seu projeto tenha uma conta de faturamento. Portanto, você também precisa de uma conta de faturamento para instalar uma extensão.
- Em seu novo projeto, habilite o Real-time Database .
- Como você deseja testar a capacidade da sua extensão de preencher os dados existentes na instalação, importe alguns dados de amostra para sua instância de banco de dados em tempo real:
- Baixe alguns dados iniciais do RTDB .
- Na página Banco de dados em tempo real do console do Firebase, clique em (mais) > Importar JSON e selecione o arquivo que você acabou de baixar.
Para permitir que a função de preenchimento use o método
orderByChild
, configure o banco de dados para indexar mensagens no valor deupper
:{ "rules": { ".read": false, ".write": false, "messages": { ".indexOn": "upper" } } }
Agora instale sua extensão da fonte local no novo projeto:
Crie um novo diretório para seu projeto do Firebase:
mkdir ~/extensions-live-test && cd ~/extensions-live-test
Inicialize um projeto do Firebase no diretório de trabalho:
firebase init database
Quando solicitado, selecione o projeto que você acabou de criar.
Instale a extensão em seu projeto local do Firebase:
firebase ext:install /path/to/rtdb-uppercase-messages
Aqui você pode ver como é a experiência do usuário ao instalar uma extensão usando a ferramenta Firebase CLI. Certifique-se de selecionar "sim" quando a ferramenta de configuração perguntar se você deseja preencher seu banco de dados existente.
Depois de selecionar as opções de configuração, a CLI do Firebase salvará sua configuração no diretório
extensions
e registrará o local de origem da extensão no arquivofirebase.json
. Coletivamente, esses dois registros são chamados de manifesto de extensões . Os usuários podem usar o manifesto para salvar a configuração de suas extensões e implantá-las em diferentes projetos.Implante sua configuração de extensão em seu projeto ativo:
firebase deploy --only extensions
Se tudo correr bem, a CLI do Firebase deverá fazer upload da sua extensão para o seu projeto e instalá-la. Após a conclusão da instalação, a tarefa de preenchimento será executada e, em alguns minutos, seu banco de dados será atualizado com mensagens em maiúsculas. Adicione alguns novos nós ao banco de dados de mensagens e certifique-se de que a extensão também esteja funcionando para novas mensagens.
- Os usuários podem criar um manifesto de extensão usando o comando
firebase ext:install
. Você também pode usar este comando para instalar uma extensão da fonte local. - Implante uma configuração de extensão de um manifesto em um projeto ativo usando
firebase deploy
. - Embora não seja demonstrado aqui, os usuários também podem instalar extensões em seus projetos a partir do Extensions Hub.
Mais Informações
Consulte a documentação do usuário sobre como gerenciar configurações de projeto com o manifesto de extensões .
10. Escreva a documentação
Antes de compartilhar sua extensão com os usuários, certifique-se de fornecer documentação suficiente para que eles tenham sucesso.
Quando você inicializou o projeto de extensão, a CLI do Firebase criou versões stub da documentação mínima necessária. Atualize esses arquivos para refletir com precisão a extensão que você criou.
extensão.yaml
Você já está atualizando este arquivo à medida que desenvolve esta extensão, então não precisa fazer mais atualizações agora.
Porém, não negligencie a importância da documentação contida neste arquivo. Além das informações cruciais de identificação de uma extensão – nome, descrição, autor, localização oficial do repositório – o arquivo extension.yaml
contém documentação voltada ao usuário para cada recurso e parâmetro configurável pelo usuário. Essas informações são apresentadas aos usuários no console do Firebase, no Extensions Hub e na CLI do Firebase.
PRÉ-INSTALAR.md
Neste arquivo, forneça as informações que o usuário precisa antes de instalar sua extensão: descreva resumidamente o que a extensão faz, explique quaisquer pré-requisitos e forneça ao usuário informações sobre as implicações de cobrança da instalação da extensão. Se você tiver um site com informações adicionais, este também é um bom lugar para vinculá-lo.
O texto deste arquivo é exibido ao usuário no Extensions Hub e pelo comando firebase ext:info
.
Aqui está um exemplo de um arquivo 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 arquivo contém informações úteis para os usuários após a instalação bem-sucedida da extensão: por exemplo, etapas de configuração de acompanhamento, um exemplo da extensão em ação e assim por diante.
O conteúdo de POSTINSTALL.md é exibido no console do Firebase depois que uma extensão é configurada e instalada. Você pode fazer referência aos parâmetros do usuário neste arquivo e eles serão substituídos pelos valores configurados.
Aqui está um exemplo de arquivo pós-instalação para a extensão do 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.
CHANGELOG.md
Você também deve documentar as alterações feitas entre os lançamentos de uma extensão no arquivo CHANGELOG.md
.
Como a extensão de exemplo nunca foi publicada antes, o log de alterações possui apenas uma entrada:
## Version 0.0.1
Initial release of the _Convert messages to upper case_ extension.
LEIA-ME.md
A maioria das extensões também fornece um arquivo leia-me para o benefício dos usuários que visitam o repositório da extensão. você pode escrever este arquivo manualmente ou gerar um leia-me usando o comando.
Para os fins deste guia, pule a gravação de um arquivo leia-me.
Documentação adicional
A documentação discutida acima é o conjunto mínimo de documentação que você deve fornecer aos usuários. Muitas extensões exigem documentação mais detalhada para que os usuários possam usá-las com êxito. Quando for esse o caso, você deve escrever documentação adicional e hospedá-la em algum lugar onde possa direcionar os usuários.
Para os fins deste guia, evite escrever uma documentação mais extensa.
- No mínimo, cada extensão deve fornecer documentação do usuário nos seguintes arquivos:
extension.yaml
,PREINSTALL.md
,POSTINSTALL.md
eCHANGELOG.md
. - Você também deve fornecer aos usuários documentação mais detalhada quando necessário.
Mais Informações
Consulte a documentação sobre como escrever documentação .
11. Publique no Extensions Hub
Agora que sua extensão está completa e documentada, você está pronto para compartilhá-la com o mundo no Extensions Hub. Mas como este é apenas um tutorial, não faça isso. Comece a escrever sua própria extensão usando o que você aprendeu aqui e no restante da documentação do editor de extensões do Firebase e examinando a fonte das extensões oficiais escritas pelo Firebase.
Quando estiver pronto para publicar seu trabalho no Extensions Hub, veja como você fará isso:
- Se você estiver publicando sua primeira extensão, registre-se como editor de extensões . Ao se registrar como editor de extensões, você cria um ID de editor que permite aos usuários identificá-lo rapidamente como o autor de suas extensões.
Hospede o código-fonte da sua extensão em um local verificável publicamente. Quando seu código estiver disponível em uma fonte verificável, o Firebase poderá publicar sua extensão diretamente desse local. Isso ajuda a garantir que você esteja publicando a versão atualmente lançada da sua extensão e ajuda os usuários, permitindo que examinem o código que estão instalando em seus projetos.
Atualmente, isso significa disponibilizar sua extensão em um repositório público do GitHub.
Faça upload de sua extensão para o Extensions Hub usando o comando
firebase ext:dev:upload
.Acesse o painel do editor no Firebase console, encontre a extensão que você acabou de enviar e clique em "Publicar no Extensions Hub". Isso requer uma revisão de nossa equipe de revisão, o que pode levar alguns dias. Se aprovada, a extensão será publicada no Extensions Hub. Se for rejeitado, você receberá uma mensagem explicando o motivo; você poderá então resolver os problemas relatados e reenviá-los para revisão.
- Para compartilhar extensões no Extensions Hub, você deve estar registrado como editor.
- A publicação de uma fonte verificável é necessária e dá aos usuários a garantia de que o código que estão instalando é o mesmo que podem examinar no GitHub.
- Use o comando
firebase ext:dev:upload
para fazer upload de uma extensão para o Extensions Hub. - Envie suas extensões para revisão no painel do editor.
Mais Informações
Saiba mais sobre como registrar-se como editor e publicar uma extensão .