拡張機能にユーザーフックを追加する

拡張機能をインストールするユーザーは、拡張機能の実行に独自のカスタム ロジックを挿入できます。これを行うには、次の 2 つの方法があります。

  • Eventarc イベント: イベントに非同期で対応するには、イベントを Eventarc に公開します。長時間実行タスクの完了後に通知を送信するイベント ハンドラ関数や、独自の後処理関数を定義できます。

  • 同期フック: 拡張機能にブロッキング ロジックを追加するには、拡張機能の動作で事前定義されたポイントに同期フックを追加します。そのポイントでユーザー提供の関数を実行し、その完了後に処理を続行できます。前処理タスクの多くはこのカテゴリに分類されます。

拡張機能では、このいずれかまたは両方の方法を使用できます。

Eventarc イベント

拡張機能からのイベントを公開するには:

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

    type 識別子は、複数のドット区切りフィールドで構成されます。パブリッシャー ID、拡張機能の名前、イベント名のフィールドは必須です。バージョンのフィールドは推奨です。公開するイベントタイプごとに、一意でわかりやすいイベント名を選択します。

    たとえば、storage-resize-images 拡張機能は単一のイベントタイプを宣言します。

    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.
    

    ユーザーは、拡張機能のインストール時に、登録するイベントを選択できます。

  2. 拡張機能で、Admin SDK から Eventarc API をインポートし、ユーザーのインストール設定を使用してイベント チャネルを初期化します。これらの設定は、次の環境変数を使用して公開されます。

    • EVENTARC_CHANNEL: ユーザーがイベントの公開先として選択した Eventarc チャネルの完全修飾名。
    • EXT_SELECTED_EVENTS: ユーザーが公開するイベントタイプのカンマ区切りリスト。この値でチャネルを初期化すると、Admin SDK は選択されなかったイベントを自動的に除外します。
    • EVENTARC_CLOUD_EVENT_SOURCE: Cloud Event ソースの ID。Admin SDK は、公開されたイベントの source フィールドにこの値を自動的に渡します。通常、この変数を明示的に使用する必要はありません。

    インストール時にイベントが有効にされていない場合、これらの変数は未定義になります。つまり、イベントが有効になっている場合にのみ、イベント チャネルが初期化されます。

    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. 拡張機能で、ユーザーに公開したいポイントでイベントをチャネルに公開します。次に例を示します。

    // 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. 公開するイベントを、PREINSTALL ファイルまたは POSTINSTALL ファイルに記述します。

    イベントごとに次のことを記述します。

    • 本来の用途
    • 拡張機能が実行するロジックのポイント
    • 含まれる出力データ
    • 実行の条件

    また、同じ拡張機能をトリガーするイベント ハンドラ内でアクションを実行しないように、ユーザーに警告します(実行すると、無限ループが生じる可能性があります)。

拡張機能からイベントを公開すると、ユーザーはイベント ハンドラをデプロイしてカスタム ロジックで応答できます。

たとえば、次の例では、画像のサイズを変更した後に元の画像を削除します。このハンドラの例では、イベントの subject プロパティ(この場合は画像の元のファイル名)を使用しています。

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

詳細については、カスタム イベント トリガーをご覧ください。

公式の Resize Images 拡張機能は、画像サイズの変更後に Eventarc に公開することで非同期フックを提供します。

同期フック

拡張機能の関数の 1 つを機能させるために正常に完了する必要があるフックをユーザーに提供する場合は、同期フックを使用します。

同期フックは、ユーザー定義の HTTPS 呼び出し可能 Cloud Functions の関数を呼び出し、その完了を待ってから次に進みます(値が返される場合もあります)。ユーザーが指定した関数がエラーになると、拡張機能の関数もエラーになります。

同期フックを公開するには:

  1. ユーザーが Cloud Functions のカスタム関数の URL を使用して拡張機能を構成するパラメータを拡張機能に追加します。次に例を示します。

    - 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. フックを公開する拡張機能のポイントで、URL を使用して関数を呼び出します。次に例を示します。

    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. PREINSTALL ファイルまたは POSTINSTALL ファイルのいずれかでフックを作成した場合は、それらを記述します。

    フックごとに以下のことを記述します。

    • 本来の用途
    • 拡張機能が実行するロジックのポイント
    • 想定される入力と出力
    • 実行の条件(またはオプション)

    また、フック関数でアクションを実行しないように、ユーザーに警告します(実行すると、無限ループが生じる可能性があります)。

Algolia Search 拡張機能は、Algolia に書き込む前にユーザー提供の変換関数を呼び出す同期フックを提供します。