Comece a criar uma extensão

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.

  1. Instale o Node.js 16 ou mais recente. Uma maneira de instalar o Node é usando nvm (ou nvm-windows ).

  2. 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:

  1. Crie um diretório para sua extensão e cd nele:

    mkdir rtdb-uppercase-messages && cd rtdb-uppercase-messages
    
  2. 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.

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:

  1. Mude para o diretório integration-tests :

    cd functions/integration-tests
    
  2. 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.

  3. 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".

  4. 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);
    });
    
  5. 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.

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 .

  1. 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.

  2. Como você alterou o nome da sua extensão, você também deve atualizar a configuração do seu emulador com o novo nome:

    1. Em functions/integration-tests/firebase.json , altere greet-the-world para rtdb-uppercase-messages .
    2. Renomeie functions/integration-tests/extensions/greet-the-world.env para functions/integration-tests/extensions/rtdb-uppercase-messages.env .

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.

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.

  1. 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 e export ) em vez de CommonJS ( require ). Para usar módulos ES no Node, especifique "type": "module" em functions/package.json :

    {
      "name": "rtdb-uppercase-messages",
      "main": "index.js",
      "type": "module",
      …
    }
    
  2. Cada função em sua extensão deve ser declarada no arquivo extension.yaml . A extensão de exemplo declarou greetTheWorld como a única Cloud Function da extensão; agora que você o substituiu por makeuppercase , você também precisa atualizar sua declaração.

    Abra extension.yaml e adicione um 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. 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:

    1. Se o emulador ainda estiver em execução, pare-o pressionando Ctrl-C.

    2. 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.

    3. Reinicie o emulador:

      firebase emulators:start --project=demo-test
      
  4. Experimente sua extensão atualizada:

    1. Abra a UI do emulador de banco de dados usando o link que o emulador imprimiu quando você o iniciou.

    2. 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.

    3. Tente adicionar mais alguns filhos ao nó messages ( {"original":"any text"} ). Sempre que você adicionar um novo registro, a extensão deverá adicionar um campo uppercase contendo o conteúdo maiúsculo do campo original .

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.

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.

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:

  1. No arquivo extension.yaml , adicione uma seção 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
    

    Isso define um novo parâmetro de string que os usuários serão solicitados a definir quando instalarem sua extensão.

  2. Ainda no arquivo extension.yaml , volte para sua declaração makeuppercase e altere o campo resource 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ção makeuppercase 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 em extension.yaml (e em POSTINSTALL.md —mais sobre isso mais tarde).

  3. 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.

  4. 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.
        ...
    
  5. 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ção GREETING 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.

  6. 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.

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:

  1. 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 use test-publisher por enquanto.)

  2. 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. se EVENTARC_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 de data 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.

  3. Normalmente, as variáveis ​​de ambiente EVENTARC_CHANNEL e EXT_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 arquivo rtdb-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:

  1. 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.

  2. 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.

  3. 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".

  4. 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ção extraemphasis do usuário devem ser acionadas em sequência, resultando no campo upper recebendo o valor RECIPE!!! .

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:

  1. 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.

  2. No arquivo extension.yaml , declare sua função de preenchimento como um recurso de extensão que possui a propriedade taskQueueTrigger :

    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
    
  3. 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!

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:

  1. No console do Firebase , adicione um novo projeto.
  2. 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.
  3. Em seu novo projeto, habilite o Real-time Database .
  4. 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:
    1. Baixe alguns dados iniciais do RTDB .
    2. 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.
  5. Para permitir que a função de preenchimento use o método orderByChild , configure o banco de dados para indexar mensagens no valor de upper :

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

Agora instale sua extensão da fonte local no novo projeto:

  1. Crie um novo diretório para seu projeto do Firebase:

    mkdir ~/extensions-live-test && cd ~/extensions-live-test
    
  2. Inicialize um projeto do Firebase no diretório de trabalho:

    firebase init database
    

    Quando solicitado, selecione o projeto que você acabou de criar.

  3. 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 arquivo firebase.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.

  4. 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.

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.

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:

  1. 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.
  2. 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.

  3. Faça upload de sua extensão para o Extensions Hub usando o comando firebase ext:dev:upload .

  4. 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.