SlackでのExperience Platform イベントのモニタリング

Adobe App Builder Webhook プロキシと統合して、SlackでExperience Platform通知を受け取る方法を説明します。 データエンジニアや管理者は、Platform実装の健全性をモニタリングするために、Adobe Experience PlatformからSlackでプロアクティブな通知を受け取る必要があります。 このチュートリアルでは、Adobe App Builderを使用してAdobe I/O EventsをSlackに接続するためのアーキテクチャと実装手順について説明します。

なぜWebhook プロキシなのか?

Adobe I/O EventsをSlack Incoming Webhookに直接接続することは、検証プロセスのプロトコルが一致しないため不可能です。

  • チャレンジ: Adobe I/O EventsでWebhookを登録すると、Adobeはエンドポイントに「チャレンジ」リクエスト(GETまたはPOST)を送信します。 エンドポイントはこの課題を正常に処理し、特定の値を返して所有権を確認する必要があります。

  • 制限: Slackの受信Webhookは、メッセージ用にJSON ペイロードを取り込むためにのみ設計されています。 Adobeの検証課題のハンドシェイクを認識したり、対応したりするロジックがありません。

  • 解決策: Adobe App Builderを使用して、中間的なWebhook プロキシをデプロイします。 このプロキシは、AdobeとSlackの間に配置され、次の操作を行います。

    1. Adobeからのリクエストを受け取り、検証の課題に対応します。
    2. ペイロードをSlack互換メッセージにフォーマットし、Slackに転送します。
  • 配信方法:実行時アクション。 ランタイムアクションは、Adobe App Builderにパッケージ化されています。 ランタイムアクション(web アクション以外)をイベントハンドラーとして使用すると、Adobe I/O Eventsは署名検証とチャレンジレスポンスを自動的に処理します。 ランタイムアクションは、必要なコードが少なく、組み込みのセキュリティを提供するため、推奨されるアプローチです。

アーキテクチャの概要

Adobe Developer Consoleとは?

Adobe Developer Consoleは、Adobe プロジェクト、API、資格情報を管理するための中央ポータルです。 これは、プロジェクトの作成、認証の設定、Webhookの登録をおこなう場所です。

App Builderとは?

Adobe App Builderは、開発者がクラウドネイティブなアプリケーションを構築するための包括的なフレームワークです。

  • プロビジョニング: App Builderはデフォルトで有効になっていません。機能として組織にプロビジョニングする必要があります。 組織に​ App Builder ​の使用権限があることを確認してください。

  • プロジェクトテンプレート: App Builder プロジェクトは、Developer Consoleの​App Builder テンプレート(​ テンプレートからのプロジェクト ​ > App Builder)を使用して作成されます。 テンプレートは、必要なワークスペースとランタイム環境を自動的に設定します。

  • App Builder入門ガイド:テンプレート 🔗から最初のプロジェクトをプロビジョニングして作成するには、 ドキュメントを参照してください。

Adobe I/O Runtimeとは?

Adobe I/O Runtimeは、App Builderの能力を支えるサーバーレスプラットフォームです。 開発者は、サーバーインフラストラクチャを管理することなく、HTTP要求に応答して実行されるコード(サービスとしての機能)をデプロイできます。

この実装では、Action​を使用します。 アクションは、Adobe I/O Runtime上で実行されるステートレス関数(Node.jsで記述)です。 アクションは、Adobe I/O Eventsが接続するパブリック HTTP エンドポイントとして機能します。

詳しくは、Adobe I/O Runtimeのドキュメント ​を参照してください。

実装ガイド

前提条件

始める前に、次の項目を確認してください。

  • Adobe Developer Console アクセス: App Builderが有効になっている組織内のシステム管理者または開発者ロール ​にアクセスできる必要があります。

    note tip
    TIP
    App Builder プロビジョニングを検証するには、Adobe Developer Consoleにログインし、目的の組織に属していることを確認し、テンプレートからプロジェクトを作成​を選択して、App Builder テンプレートが使用可能であることを確認します。 そうでない場合は、「App Builderの入手方法」のApp Builder FAQ セクションを確認してください。
  • Node.jsとnpm:このプロジェクトには、NPM (Node Package Manager)を含むNode.jsが必要です。 NPMは、Adobe CLIのインストールとプロジェクトの依存関係の管理に使用されます。

  • aio CLI:端末を介してインストールされました:npm install -g @adobe/aio-cli

  • Slack アプリ設定: 受信Webhook​がアクティブ化されたワークスペースでSlack アプリを設定する必要があります。

手順1:Adobe Developer Consoleでのプロジェクトの作成

まず、Adobe Developer ConsoleでApp Builder テンプレートを使用してプロジェクトを作成します。

  1. Adobe Developer Consoleにログインします
  2. 「テンプレートからプロジェクトを作成」を選択
  3. App Builder テンプレートを選択します
  4. プロジェクト タイトル (例:Slack webhook integration)を入力します
  5. 保存​を選択

ランタイム環境の初期化

ターミナルで次のコマンドを実行して、プロジェクト構造を作成します。

aioにログイン

aio login

手順2:新しいApp Builder プロジェクトの初期化

aio app init slack-webhook-proxy
  1. 組織を選択し、Enter​を押します

  2. 前の手順で作成したプロジェクト (例:Slack webhook integration)を選択し、Enter​を押します

  3. 「組織でサポートされているテンプレートのみ」オプションを選択します

  4. Enter​を押してサンプルセクションをスキップします

  5. プロンプトが表示されたら、次のコンポーネントが選択されていることを確認し(円を入力する必要があります)、Enter​を押します。

    1. アクション:ランタイムアクションのデプロイ
    2. イベント:Adobe I/O Eventsに公開
    3. Web Assets: ホストされた静的アセットにデプロイ
  6. 「上」矢印と「下」矢印を使用して、受信リストに移動し、Adobe Experience Platform: リアルタイム顧客プロファイル​を選択し、Enter​を押します

  7. 汎用 アクションは自動的に生成されます

  8. UIの​ Pure HTML/JS ​を選択し、Enter​を押します

  9. 外部API名へのアクセス方法を示すサンプルアクションとして​ generic ​を保持し、Enter​を押します

  10. クラウドイベント形式でメッセージを作成するサンプルアクション名として​ publish-events ​を保持し、Enter​を押します

アプリの初期化が完了します。

プロジェクトディレクトリに移動します

cd slack-webhook-proxy

Web アクションを追加

aio app add action
  1. 自分の組織でサポートされているテンプレートのみ​を選択し、Enter​を押します
  2. 表示されたテーブルの​publish-events アクションを参照してください。Space​を押してアクションを選択します。 ビデオチュートリアルに示すように、名前の横にある円が入力されている場合は、Enter​を押します
  3. アクションにwebhook-proxyという名前を付けます

手順3:プロキシアクションコードの更新

IDEまたはテキストエディターで、次のコードを使用してファイル actions/webhook-proxy/index.jsを作成または変更します。 この実装は、Slackにイベントを転送します。 ランタイムアクション登録を使用すると、署名の検証とチャレンジ処理が自動的に行われます。

const fetch = require("node-fetch");
const { Core } = require("@adobe/aio-sdk");

/**
 * Adobe I/O Events to Slack Runtime Proxy
 *
 * Receives events from Adobe I/O Events and forwards them to Slack.
 * Signature verification and challenge handling are automatic when
 * using Runtime Action registration (non-web action).
 */
async function main(params) {
  const logger = Core.Logger("webhook-proxy", { level: params.LOG_LEVEL || "info" });

  try {
    logger.info(`Event received: ${JSON.stringify(params)}`);

    // Forward to Slack
    return forwardToSlack(params, params.SLACK_WEBHOOK_URL, logger);

  } catch (error) {
    logger.error(`Error: ${error.message}`);
    return { statusCode: 500, body: { error: "Internal server error" } };
  }
}

/**
 * Forwards the event payload to Slack
 */
async function forwardToSlack(payload, webhookUrl, logger) {
  if (!webhookUrl) {
    logger.error("SLACK_WEBHOOK_URL not configured");
    return { statusCode: 500, body: { error: "Server configuration error" } };
  }

  // Extract Adobe headers passed to runtime action
  const headers = {
    "x-adobe-event-code": payload["x-adobe-event-code"],
    "x-adobe-event-id": payload["x-adobe-event-id"],
    "x-adobe-provider": payload["x-adobe-provider"]
  };

  const slackMessage = buildSlackMessage(payload, headers);

  const response = await fetch(webhookUrl, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(slackMessage)
  });

  if (!response.ok) {
    const errorText = await response.text();
    logger.error(`Slack API error: ${response.status} - ${errorText}`);
    return { statusCode: response.status, body: { error: errorText } };
  }

  logger.info("Event forwarded to Slack");
  return { statusCode: 200, body: { success: true } };
}

/**
 * Builds a Slack Block Kit message from the event payload
 */
function buildSlackMessage(payload, headers) {
  // Adobe passes event code as x-adobe-event-code header (available in params for runtime actions)
  const eventType = headers["x-adobe-event-code"] ||
                    payload["x-adobe-event-code"] ||
                    payload.event_code ||
                    payload.type ||
                    payload.event_type ||
                    "Adobe Event";
  const eventId = headers["x-adobe-event-id"] || payload["x-adobe-event-id"] || payload.event_id || payload.id || "N/A";
  const eventData = payload.data || payload.event || payload;

  return {
    blocks: [
      {
        type: "header",
        text: { type: "plain_text", text: `Event: ${eventType}`, emoji: true }
      },
      {
        type: "section",
        fields: formatDataFields(eventData)
      },
      { type: "divider" },
      {
        type: "context",
        elements: [{
          type: "mrkdwn",
          text: `*Event ID:* ${eventId}  |  *Time:* ${new Date().toISOString()}`
        }]
      }
    ]
  };
}

/**
 * Formats event data as Slack mrkdwn fields
 */
function formatDataFields(data, maxFields = 10) {
  if (typeof data !== "object" || data === null) {
    return [{ type: "mrkdwn", text: `*Payload:*\n${String(data)}` }];
  }

  const entries = Object.entries(data);
  if (entries.length === 0) {
    return [{ type: "mrkdwn", text: "_No data provided_" }];
  }

  return entries.slice(0, maxFields).map(([key, value]) => ({
    type: "mrkdwn",
    text: `*${key}:*\n${typeof value === "object" ? `\`\`\`${JSON.stringify(value)}\`\`\`` : value}`
  }));
}

exports.main = main;

手順4:アクションの設定

app.config.yamlのアクション設定は重要です。 Web: noを使用して、Developer Consoleでランタイムアクションとして登録できる非web アクションを作成します。

application:
  runtimeManifest:
    packages:
      slack-webhook-proxy:
        license: Apache-2.0
        actions:
          webhook-proxy:
            function: actions/webhook-proxy/index.js
            web: no
            runtime: nodejs:22
            inputs:
              LOG_LEVEL: info
              SLACK_WEBHOOK_URL: $SLACK_WEBHOOK_URL
            annotations:
              require-adobe-auth: false
              final: true

web: no?の理由

Web以外のアクションを使用し、Developer Consoleの「ランタイムアクション」オプションを使用して登録すると、Adobe I/O Eventsは自動的に次の操作を行います。

  • チャレンジの検証を処理します(GETとPOST)
  • アクションを呼び出す前にデジタル署名を確認します
  • アクションの前に署名バリデーターアクションを連結します

つまり、コードはビジネスロジック(Slackへの転送)のみを処理する必要があります。

手順4:環境変数の更新

資格情報を安全に管理するには、環境変数を使用します。 プロジェクトのルートにある.env ファイルを作成または変更して、Slack Webhook URLを追加します。 .env ファイルが表示されない場合は、システムに隠しファイルを表示するようにしてください。

# ... other .env file content ...

SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL

手順5:アクションのデプロイ

環境変数を設定したら、アクションをデプロイします。 ターミナルでこのコマンドを実行する場合は、プロジェクトのルート、つまりslack-webhook-proxyに必ず配置してください。

aio app deploy

アクションはAdobe I/O Runtimeにデプロイされ、Developer Consoleで登録できます。

手順6:Adobe Developer Consoleでアクションを登録する

アクションがデプロイされたら、Adobe Eventsの宛先として登録します。

  1. Adobe Developer Consoleに移動し、App Builder プロジェクトを開きます。

  2. Workspace​を選択

  3. 「サービスを追加」を選択し、「イベント」を選択します。

  4. 商品として​ Adobe Experience Platform ​を選択します。

  5. イベントのタイプとして「プラットフォーム通知」を選択します。

  6. Slackで通知する特定のイベント(またはすべて)を選択し、次へ​を選択します。

  7. OAuth資格情報🔗を選択または作成します。

  8. イベント登録の詳細​を設定します。

    1. 登録名:登録にわかりやすい名前を付けます。
    2. 登録の説明:他のコントリビューターが自分の行動を把握できるように、これが明示的であることを確認してください。
    3. 次へ​を選択
    4. 配信方法: ランタイムアクション​を選択します(「Webhook」ではありません)。
    5. 実行時アクション: ドロップダウンからwebhook-proxyを選択します(表示されない場合はページを更新します)。
  9. 設定されたイベントを保存​を選択します。

手順7:サンプルイベントによる検証

設定されたイベントの横にある「サンプルイベントを送信」アイコンをクリックすると、フロー全体をエンドツーエンドでテストできます。

サンプルイベントは、Slack アプリの作成時およびWebhookの作成時に設定したチャネルで送信されます。次のような内容が表示されます。

Slackでのモニタリングイベントの例

SlackとExperience Platform eventsの統合に成功しました。

一般的な問題

ここでは、プロキシを設定する際に発生する可能性のある問題と、その対処方法について説明します。

  • IMS組織を表示できません: aio app initの実行時にIMS組織のリストが表示されない場合は、次の操作を試してください。 ターミナルでaio logoutを実行し、デフォルトのweb ブラウザーでExperience Cloudからログアウトして、aio loginを再度実行します。
  • アクションがドロップダウンに表示されません: web: noがapp.config.yamlに設定されていることを確認してください。 ランタイムアクション ドロップダウンには、Web以外のアクションのみが表示されます。 デプロイ後にDeveloper Console ページを更新します。
  • 署名の検証に失敗しました:これをアクティベーションログに表示すると、Adobeの組み込みバリデーターがリクエストを拒否したことを意味します。 これは、Adobeの正当なイベントのために行われるべきではありません。 イベント登録が正しく設定されていることを確認します。
  • Slackがメッセージを受信しません: SLACK_WEBHOOK_URLが.env ファイルで正しく設定されており、Slack アプリで受信Webhookが有効になっていることを確認してください。
  • アクションのタイムアウト:実行時アクションのタイムアウトは60秒です。 アクションに時間がかかる場合は、代わりにジャーナリングアプローチを使用することを検討してください。
recommendation-more-help
platform-learn-help-platform