MomentoのWebhookを試してみる
Introduction
MomentoでWebhook機能が使えるようになりました!
Webhookを使えば、Momento Topicsへpublishしたタイミングで
任意のエンドポイントのアクションを実行することができます。
例えば、こちらの記事では、Momento TopicsとWebhookを利用した多言語チャットのデモを紹介しています。
デモではMomento Topicsへメッセージをpublishすると、
WebhookでAWS Lambdaが起動し、各言語へ翻訳してそれぞれのTopicにpublishします。
ユーザーは希望する言語(デモ画面右上)のTopicをsubscribeしているので、
選択した言語でメッセージを見ることができます。
本稿ではngrokとTypeScriptで実装したアプリを使って、
Momento Webhook機能を確認してみます。
Momento?
Momentoはこのblogで紹介している、
クラウドネイティブな各種サーバレスサービスです。
Data Caceh,メッセージング(Topics),Vectorデータベース(Vector Index)と、
用途に応じて各種サービスを提供しています。
Environment
- MacBook Pro (13-inch, M1, 2020)
- OS : MacOS 13.5.2
- ngrok : 3.4.0
- node : v20.8.1
Setup
Momentoの設定
まずはMomentoキャッシュを作成します。
Momento Consoleにアクセスしましょう。
アカウントがまだないならここでSignupしてログインします。
ログインしたら、キャッシュ一覧からキャッシュ作成を選択し、
キャッシュ名を記述してクラウドプロバイダーとリージョンを指定します。
今回は「webhook_cache」という名前のキャッシュを作成しました。
次に、トークンのページにアクセスして、
先ほど作成したキャッシュのアクセス権限を持つ
Access Tokenを生成します。
「トークンを生成する」ボタンをクリックするとトークンが生成されるので、
環境変数に設定しておきましょう。
% export MOMENTO_TOKEN="<Auth Token>"
Webhook作成
作成したキャッシュの画面左メニューから「Webhooks」を選択します。
「Webhookの作成」ボタンを押して作成画面へいきましょう。
Webhook名は適当に決めて、トピック名は「webhook_topic」とします。
「Webhookの宛先」はあとで修正するので、適当に設定します。
Webhookを生成すると、それに対応するシークレット文字列が生成されます。
この文字列を使うと、リクエストがMomento から発信されたものかどうか検証できます。
これも覚えておきましょう。
ngrokの設定
ngrok(エングロック)はローカルで動作しているWebサーバを、
パブリックインターネット上に一時的に公開できるツールです。
例えば、自分のPCで開発しているWebアプリケーションを手軽に他の人に見せたり、
今回のようなWebhookサービスを試すときに便利です。
ngrokを起動すると公開用のURLが作成されるので、
そのURLを使用してローカル環境にアクセスできます。
このURLは、ngrokを停止するまで有効となります。
MacならHomebrewでインストールできます。
% brew install ngrok/ngrok/ngrok
あとはsignupしてauthtokenを取得して設定します。
ここなどを確認して設定しましょう。
設定できたら、↓のように起動できます。
これは、ローカルの3000番ポートを
インターネットに公開することになります。
% ngrok http 3000
起動すると下記のような情報が表示されます。
Forwardingに、実際に割り当てられたアドレスが表示されます。
ngrok (Ctrl+C to quit) Build better APIs with ngrok. Early access: ngrok.com/early-access Session Status online Account xxxxxx (Plan: Free) Version 3.4.0 Region United States (us) Latency 881ms Web Interface http://127.0.0.1:4040 Forwarding https://xxxxxx.ngrok-free.app -> http://localhost:3000 Connections ttl opn rt1 rt5 p50 p90 2 0 0.02 0.01 0.03 0.05 HTTP Requests ------------- GET /favicon.ico 404 File not found GET / 200 OK
3000番ポートをフォワードするように起動し、
割り当てられたアドレスをMomentoのWebhook画面の
Webhookの宛先に設定しましょう。
これで事前準備は完了です。
Try
では、ngrokからフォワードする先のWebアプリを実装します。
まずは必要なモジュールをインストールしておきます。
% npm install @gomomento/sdk crypto --save % npm install typescript ts-node @types/node --save-dev
最初はexpress使おうとしたんですが、
なぜかWebhookからPOSTのbodyが受け取れなかったので
httpモジュール使ってます。
POSTのリクエストを受けると、bodyをパースして、
うけとった値(body.text)を表示してます。
また、didRequestComeFromMomentoの結果がtrueであれば、
Webhookからきたリクエストだと判断されます。
//index.ts import http from 'http'; import crypto from 'crypto'; //Webhookからのリクエストかどうかチェックする function didRequestComeFromMomento (req:any,rawBody:string): boolean { const hash = crypto.createHmac("SHA3-256", "<Webhook生成時のシークレット文字列>"); const hashed = hash.update(rawBody).digest('hex'); return hashed === req.headers['momento-signature']; } const server = http.createServer((req, res) => { if (req.url === '/' && req.method === 'POST') { let body = ''; req.on('data', chunk => { body += chunk.toString(); // raw body }); req.on('end', () => { console.log('Body.text:', JSON.parse(body).text); console.log("didRequestComeFromMomento : " + didRequestComeFromMomento(req,body)); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ message: 'hello' })); }); } else { res.writeHead(404); res.end(); } }); const port = 3000; server.listen(port, () => console.log(`Server is running at http://localhost:${port}`));
デモではWebhookで起動されたLambdaがメッセージを各言語に翻訳して、
各言語用のTopicsにそれぞれ対応したメッセージをpublishしてます。
作成したモジュールを起動させておきます。
% npx ts-node index.ts Server is running at http://localhost:3000
この時点で、ngrokから割り当てられたアドレスにアクセスすると
ローカルのサーバにフォワードされます。
ではMomento Topicsにメッセージをpublishして
Webhookを起動してみましょう。
Topicsにpublushするサンプルコードは下記です。
//pub.ts import { TopicClient, TopicPublish, Configurations, CredentialProvider, } from '@gomomento/sdk'; const cacheName = "webhook_cache"; const topicName = "webhook_topic"; async function main() { const momento = new TopicClient({ configuration: Configurations.Laptop.v1(), credentialProvider: CredentialProvider.fromEnvironmentVariable({ environmentVariableName: 'MOMENTO_TOKEN', }), }); const message = { 'message': "bar" }; console.log( `Publishing cacheName=${cacheName}, topicName=${topicName}, value=${message}` ); const publishResponse = await momento.publish( cacheName, topicName, JSON.stringify(message)); if (publishResponse instanceof TopicPublish.Success) { console.log('Value published successfully!'); } else { console.log(`Error publishing value: ${publishResponse.toString()}`); } } main() .then(() => { console.log('publish success.'); }) .catch((e: Error) => { console.error(`Uncaught exception while running example: ${e.message}`); throw e; });
さきほど作成したMomento Cache名とTopics名を指定してpublishしてます。
では実行してみましょう。
% npx ts-node pub.ts
WEBサーバにWebhookリクエストがきており、
ログがでているはずです。
# WEBサーバのログ ・・・ Body.text: {"message":"bar"} "event_timestamp":1701762660904,"publish_timestamp":1701762624505,"topic_sequence_number":1,"token_id":"","text":"{\"message\":\"bar\"}"} didRequestComeFromMomento : true
ちなみに、プログラムでwebhook_topicをsubscribeしておけば
普通にpublishされたメッセージを受け取ることができます。
Summary
今回はMomentoの新機能、Webhookを動かしてみました。
デモでは多言語翻訳チャットを実装していますが、
他にもいろいろな使い方ができそうな機能です。