Ajouter des hooks utilisateur à une extension

Vous pouvez offrir aux utilisateurs qui installent votre extension la possibilité d'insérer leur propre logique personnalisée dans l'exécution de votre extension. Il existe deux manières d'y parvenir :

  • Événements Eventarc : pour donner aux utilisateurs un moyen de réagir de manière asynchrone aux événements, vous pouvez publier sur Eventarc. Les utilisateurs peuvent déployer des fonctions de gestionnaire d'événements qui, par exemple, envoient des notifications une fois les tâches de longue durée terminées, ou ils peuvent définir leurs propres fonctions de post-traitement.

  • Hooks synchrones : pour donner aux utilisateurs un moyen d'ajouter une logique de blocage à votre extension, vous pouvez ajouter des hooks synchrones à des points prédéfinis dans le fonctionnement de l'extension. À ces stades, vous exécutez une fonction utilisateur-fournisseur et ne continuez qu’une fois celle-ci terminée. Les tâches de prétraitement entrent souvent dans cette catégorie.

Une extension peut utiliser l’une ou les deux méthodes.

Événements Eventarc

Pour publier des événements à partir d'une extension :

  1. Déclarez les types d'événements que vous publierez dans le fichier 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
    

    L'identifiant type est composé de plusieurs champs délimités par des points. Les champs identifiant l'éditeur , le nom de l'extension et le nom de l'événement sont obligatoires. Le champ version est recommandé. Choisissez un nom d'événement unique et descriptif pour chaque type d'événement que vous publiez.

    Par exemple, l' extension storage-resize-images déclare un seul type d'événement :

    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.
    

    Les utilisateurs pourront choisir les événements auxquels s'abonner lorsqu'ils installeront l'extension.

  2. Dans vos fonctions d'extension, importez l'API Eventarc depuis le SDK Admin et initialisez un canal d'événement à l'aide des paramètres d'installation de l'utilisateur. Ces paramètres sont exposés à l'aide des variables d'environnement suivantes :

    • EVENTARC_CHANNEL : le nom complet du canal Eventarc sur lequel l'utilisateur a choisi de publier des événements.
    • EXT_SELECTED_EVENTS : une liste de types d'événements séparés par des virgules que l'utilisateur a choisi de publier. Lorsque vous initialisez un canal avec cette valeur, le SDK Admin filtre automatiquement les événements que l'utilisateur n'a pas sélectionnés.
    • EVENTARC_CLOUD_EVENT_SOURCE : l'identifiant de la source de l'événement Cloud. Le SDK Admin transmet automatiquement cette valeur dans le champ source des événements publiés. Vous n'avez généralement pas besoin d'utiliser explicitement cette variable.

    Si les événements n'ont pas été activés lors de l'installation, ces variables ne seront pas définies. Vous pouvez utiliser ce fait pour initialiser un canal d'événements uniquement lorsque les événements sont activés :

    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. Publiez des événements sur le canal aux points de votre extension que vous souhaitez exposer aux utilisateurs. Par exemple:

    // 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. Documentez les événements que vous publiez, dans le fichier PREINSTALL ou POSTINSTALL.

    Pour chaque événement, documentez les éléments suivants :

    • Son objectif prévu
    • Le point dans la logique de votre extension qu'il exécute
    • Les données de sortie qu'il comprend
    • Les conditions de son exécution

    De plus, avertissez les utilisateurs de ne pas effectuer d'actions dans leurs gestionnaires d'événements qui pourraient déclencher la même extension, ce qui entraînerait une boucle infinie.

Lorsque vous publiez des événements à partir d'une extension, les utilisateurs peuvent déployer des gestionnaires d'événements pour répondre avec une logique personnalisée.

Par exemple, l'exemple suivant supprime l'image d'origine après son redimensionnement. Notez que cet exemple de gestionnaire utilise la propriété subject de l'événement, qui dans ce cas est le nom de fichier d'origine de l'image.

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();
    });

Voir Déclencheurs d'événements personnalisés pour plus d'informations.

Exemple

L' extension officielle Resize Images fournit un hook asynchrone en publiant sur Eventarc après avoir redimensionné une image.

Crochets synchrones

Lorsque vous souhaitez fournir aux utilisateurs un hook qui doit se terminer avec succès pour qu'une de vos fonctions d'extension fonctionne, utilisez des hooks synchrones .

Un hook synchrone appelle une fonction Cloud appelable HTTPS définie par l'utilisateur et attend la fin (éventuellement avec une valeur renvoyée) avant de continuer. Une erreur dans la fonction fournie par l'utilisateur entraîne une erreur dans la fonction d'extension.

Pour exposer un hook synchrone :

  1. Ajoutez un paramètre à votre extension qui permet aux utilisateurs de configurer l'extension avec l'URL de leur fonction Cloud personnalisée. Par exemple:

    - 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. Au point de votre extension où vous souhaitez exposer le hook, appelez la fonction en utilisant son URL. Par exemple:

    const functions = require('firebase-functions');
    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. Documentez tous les hooks que vous rendez disponibles dans le fichier PREINSTALL ou POSTINSTALL.

    Pour chaque crochet, documentez les éléments suivants :

    • Son objectif prévu
    • Le point dans la logique de votre extension qu'il exécute
    • Ses entrées et sorties attendues
    • Les conditions (ou options) de son exécution

    De plus, avertissez les utilisateurs de ne pas effectuer d’actions dans la fonction hook qui pourraient déclencher la même extension, entraînant une boucle infinie.

Exemple

L' extension Algolia Search fournit un hook synchrone pour appeler une fonction de transformation fournie par l'utilisateur avant d'écrire dans Algolia.