Adicionar ganchos do usuário a uma extensão

É possível fornecer aos usuários que instalam sua extensão a capacidade de inserir a própria lógica personalizada na execução da extensão. Há duas maneiras de fazer isso:

  • Eventos do Eventarc: é possível publicar eventos no Eventarc para permitir que os usuários reajam de maneira assíncrona. Os usuários podem implantar funções de manipulador de eventos que, por exemplo, enviam notificações após a conclusão de tarefas de longa duração ou podem definir as próprias funções de pós-processamento.

  • Ganchos síncronos: para dar aos usuários uma maneira de adicionar lógica de bloqueio à sua extensão, você pode adicionar ganchos síncronos em pontos predefinidos na operação da extensão. Nesse ponto, você executa uma função de provedor de usuário e continua somente após a conclusão. As tarefas de pré-processamento geralmente se enquadram nessa categoria.

Uma extensão pode usar um dos métodos ou ambos.

Eventos do Eventarc

Para publicar eventos usando uma extensão, siga estas instruções:

  1. Declare os tipos de evento que serão publicados no arquivo extension.yaml:

    events:
      - type: publisher-id.extension-name.version.event-name
        description: event-description
      - type: publisher-id.extension-name.version.another-event-name
        description: another-event-description
    

    O identificador type é composto por vários campos delimitados por pontos. Os campos ID do editor, nome da extensão e nome do evento são obrigatórios. O campo de versão é recomendado. Escolha um nome de evento exclusivo e descritivo para cada tipo de evento publicado.

    Por exemplo, a extensão storage-resize-images declara um único tipo de evento:

    events:
      - type: firebase.extensions.storage-resize-images.v1.complete
        description: |
          Occurs when image resizing completes. The event will contain further
          details about specific formats and sizes.
    

    Os usuários poderão escolher em quais eventos se inscrever quando instalarem a extensão.

  2. Nas funções de extensão, importe a API Eventarc do Admin SDK e inicialize um canal de evento usando as configurações de instalação do usuário. Essas configurações são expostas usando as seguintes variáveis de ambiente:

    • EVENTARC_CHANNEL: o nome totalmente qualificado do canal do Eventarc em que o usuário escolheu publicar eventos.
    • EXT_SELECTED_EVENTS: uma lista separada por vírgulas de tipos de evento que o usuário escolhe publicar. Quando você inicializa um canal com esse valor, o SDK Admin filtra automaticamente os eventos que o usuário não selecionou.
    • EVENTARC_CLOUD_EVENT_SOURCE: o identificador de origem do evento do Cloud. O SDK Admin transmite automaticamente esse valor no campo source dos eventos publicados. Normalmente, não é necessário usar explicitamente essa variável.

    Se os eventos não foram ativados na instalação, essas variáveis serão indefinidas. Use esse fato para inicializar um canal de evento somente quando os eventos estiverem ativados:

    import * as admin from "firebase-admin";
    import {getEventarc} from 'firebase-admin/eventarc';
    
    admin.initializeApp();
    
    // 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,
      });
    
  3. Publique eventos no canal nos pontos da extensão que você quer expor aos usuários. Por exemplo:

    // If events are enabled, publish a `complete` event to the configured
    // channel.
    eventChannel && eventChannel.publish({
        type: 'firebase.extensions.storage-resize-images.v1.complete',
        subject: filename,  // the name of the original file
        data: {
          // ...
        }
    });
    
  4. Documente os eventos publicados no arquivo PREINSTALL ou POSTINSTALL.

    Para cada evento, documente o seguinte:

    • A finalidade
    • O ponto na lógica da sua extensão executada
    • Os dados de saída incluídos
    • As condições de execução

    Além disso, avise os usuários para não executar nenhuma ação nos manipuladores de eventos que possam acionar a mesma extensão, resultando em um loop infinito.

Quando você publica eventos de uma extensão, os usuários podem implantar manipuladores de eventos para responder com lógica personalizada.

Por exemplo, o exemplo a seguir exclui a imagem original depois que ela é redimensionada. Observe que esse gerenciador de exemplo usa a propriedade subject do evento, que nesse caso é o nome de arquivo original da imagem.

exports.onimageresized = onCustomEventPublished(
    "firebase.extensions.storage-resize-images.v1.complete",
    (event) => {
      logger.info("Received image resize completed event", event);
      // For example, delete the original.
      return admin.storage()
          .bucket("my-project.appspot.com")
          .file(event.subject)
          .delete();
    });

Consulte Acionadores de eventos personalizados para mais informações.

Exemplo

A extensão Resize Images oficial fornece um hook assíncrono ao publicar no Eventarc depois de redimensionar uma imagem.

Ganchos síncronos

Quando você quiser fornecer aos usuários um gancho que precise ser concluído com sucesso para que uma das funções de extensão funcione, use ganchos síncronos.

Um hook síncrono chama uma função do Cloud chamável HTTPS definida pelo usuário e aguarda a conclusão (possivelmente com um valor retornado) antes de continuar. Um erro na função fornecida pelo usuário resulta em um erro na função de extensão.

Para expor um hook síncrono, siga estas instruções:

  1. Adicione um parâmetro à extensão que permita aos usuários configurar a extensão com o URL para a Função do Cloud personalizada. Por exemplo:

    - param: PREPROCESSING_FUNCTION
      label: Pre-processing function URL
      description: >
        An HTTPS callable function that will be called to transform the input data
        before it is processed by this function.
      type: string
      example: https://us-west1-my-project-id.cloudfunctions.net/preprocessData
      required: false
    
  2. No ponto da extensão em que você quer expor o gancho, chame a função usando o URL dela. Por exemplo:

    const functions = require('firebase-functions/v1');
    const fetch = require('node-fetch');
    
    const preprocessFunctionURL = process.env.PREPROCESSING_FUNCTION;
    
    exports.yourFunctionName = functions.firestore.document("collection/{doc_id}")
        .onWrite((change, context) => {
          // PREPROCESSING_FUNCTION hook begins here.
          // If a preprocessing function is defined, call it before continuing.
          if (preprocessFunctionURL) {
            try {
              await fetch(preprocessFunctionURL); // Could also be a POST request if you want to send data.
            } catch (e) {
              // Preprocessing failure causes the function to fail.
              functions.logger.error("Preprocessor error:", e);
              return;
            }
          }
          // End of PREPROCESSING_FUNCTION hook.
    
          // Main function logic follows.
          // ...
        });
    
  3. Documente todos os hooks disponibilizados no arquivo PREINSTALL ou POSTINSTALL.

    Para cada hook, documente o seguinte:

    • A finalidade
    • O ponto na lógica da sua extensão executada
    • Entradas e resultados esperados
    • Condições (ou opções) de execução

    Além disso, avise os usuários para não executar nenhuma ação na função de gancho que possa acionar a mesma extensão, resultando em um loop infinito.

Exemplo

A extensão Algolia Search fornece um hook síncrono para chamar uma função de transformação fornecida pelo usuário antes de gravar no Algolia.