Actions on Google のドキュメントやチュートリアルだとあまり詳しく書かれていない、Firebase Functionsの代わりにローカルサーバーを使用する環境を構築する方法です。
コードを1行変えて動作を確認するにも毎回Firebaseにデプロイして…という手間から解放されます…!
【参考記事】
Speed up Actions on Google development workflow with local fulfillment
https://medium.com/voiceano/speed-up-actions-on-google-development-workflow-with-local-fulfilment-7de17d5f166f
実行コマンドやjsonの設定内容も上記記事から引用させていただいています。大変参考になりました。ありがとうございます!
環境
Windows 10 Home (バージョン1903)
Node v10.16.3
firebase-functions @3.2.0
firebase-admin @8.6.0
ngrok @3.2.5
nodemon @1.19.2
設定手順
躓いたポイントは後でまとめるとして、まず全部うまくいった場合の手順を。
すでに、Firebase FunctionにデプロイしてDialogflowと連動して動作しているプロジェクトがある前提で進めます。
ngrok をインストール
ローカルで実行するサーバーにDialogflowがアクセスできるようにするためのものです。
下記を実行してインストール。
npm install ngrok --save-dev
functions ディレクトリ内の package.json の "scripts"
に "tunnel"
を追加。
(おそらく scripts の項目自体はすでに存在するので、そこに足す感じで大丈夫です。)
"scripts": {
"tunnel": "ngrok http 8000"
}
これで、node.jsコンソールで functions ディレクトリに移動して npm run tunnel
を実行すると、http://localhost:8000 にアクセスできるオープンなURLが得られるようになります。
fulfillment のコードを編集して、本番用とローカル用を分ける
Actions on Googleのチュートリアル等に従って作成した感じだと、fulfillmentはindex.jsのファイルが一つあるだけだと思いますが、これを
- app.js(intent handlers. リクエストを受けてActionがどんな答えを返すかを決めるコード)
- index.js()
- local.js(ローカル実行する際に使用するExpressサーバーの設定)
の3つに分けます。
元のindex.jsがこんな感じだったら
// 元の index.js
const functions = require('firebase-functions'); // 変更前
const {
dialogflow
} = require('actions-on-google');
const app = dialogflow();
app.intent('Default Welcome Intent', (conv) => {
conv.ask("こんにちは。ご注文をどうぞ。");
});
app.intent('menu', (conv) => {
conv.ask("こちらのメニューがお選びいただけます。");
});
exports.fulfillment = functions.https.onRequest(app); // 変更前
こんな感じ。 module.exports = app
でexportします。
// app.js
// const functions = require('firebase-functions'); は index.js に
const {
dialogflow
} = require('actions-on-google');
const app = dialogflow();
app.intent('Default Welcome Intent', (conv) => {
conv.ask("こんにちは。ご注文をどうぞ。");
});
app.intent('menu', (conv) => {
conv.ask("こちらのメニューがお選びいただけます。");
});
module.exports = app; // 変更後
Firebase Functionsの環境で使う用の設定を index.js
ファイルに書きます。
// index.js
const functions = require("firebase-functions");
const app = require("./app"); // app.js から export したモジュールを使う
exports.fulfillment = functions.https.onRequest(app);
const functions = require("firebase-functions");
と exports.fulfillment = functions.https.onRequest(app);
を index.jsに残して、あとはごっそり app.js に分離する感じですね。
そしてローカルの環境で使う用の設定を local.js
ファイルに書きます。
// local.js
// テスト用にlocalで動かすExpressサーバー用のコード
const express = require('express');
const bodyParser = require('body-parser');
const app = require('./app'); // app.js から export したモジュールを使う
const myApp = express().use(bodyParser.json());
// local endpoint にアクセスできる状態かどうかの確認用
myApp.get('/', (req, res) => {
res.send('The connection was successful')
});
// Dialogflow agent からの POST request を app モジュールで扱う
myApp.post('/', app);
myApp.listen(process.env.PORT || 8000);
コードの準備はこれで完了です。
nodemon をインストール
Expressサーバーの実行、およびコードの変更を検知して、自動的にサーバーを再起動して変更を反映してくれるものです。
下記を実行してインストール。
npm install nodemon --save-dev
functions ディレクトリ内の package.json の "scripts"
に "dev"
を追加。
"scripts": {
"tunnel": "ngrok http 8000",
"dev": "nodemon local.js"
}
環境変数の設定
これは元記事にはないのですが、必要でした。
GCLOUD_PROJECT
にFirebaseのプロジェクトIDFIREBASE_CONFIG
にservice-account.json へのパス
を設定します。
コントロールパネルから設定しました。
ダブルクォートとかで囲む必要はないです。ユーザー環境変数で大丈夫でした。
npm run dev
を実行するコンソールから
set GCLOUD_PROJECT=my-app
set FIREBASE_CONFIG=C:\Path\To\my-app\functions\service-account.json
とやっても設定できますが、これだとコンソールを開きなおすと再設定が必要です。
(.bashrc的なものに書くやり方もあると思うんですが調べてません…すみません)
【参考】環境の構成 | Firebase
https://firebase.google.com/docs/functions/config-env?hl=ja#automatically_populated_environment_variables
ローカルWebサーバーの実行
ローカルでExpressサーバーを実行して、それを公開URLでアクセスできるようにします。
node.js コンソールを2つ開いて、それぞれで
npm run dev
(変更を監視する)npm run tunnel
(ローカルサーバーを公開する)
を実行します。
npm run tunnel
を実行すると以下のような画面が表示されるので、 Forwarding に表示されている https で始まるURLを、DialogflowのWebhook URLに設定します。
Dialogflow > Fulfillment > 「URL」欄
これで、Dialogflowはローカルサーバーと通信するようになります。
Firebase Functionにデプロイしたときと同じく、Actions on Googleのテスト画面で動作を確認できます。
コードに変更を加えて保存すると nodemon が自動的にリスタートして、変更が反映されます。(Firebase Functionにデプロイしなおすよりずっと速いです!)
※ngrokの無料プランでは、 ngrokを立ち上げ直すとURLが変わる ので、起動のたびにDialogflowに再設定が必要です。
接続数や連続起動しておける時間の制限もあります。でも8時間とかなので、無料プランで十分使えると思います。
※console.log も使えます。( npm run dev
したコンソールに流れてくる。)
本番(Firabase Functions)へのデプロイ
また本番につなげたいときは、下記コマンドでデプロイして、DialogflowのWebhook URLを差し替えれば、今度はFirebase Functionsと通信するようになります。
firebase deploy --only functions
設定は以上です!
躓いたポイント
npm ~ のコマンドがコンパイルエラーになる
npm 系のコマンドはnode.jsコンソールから実行するとうまくいきました。
私の環境設定が何か漏れているのかもしれませんが、Powershell やコマンドプロンプトでは、下記エラーが起きてうまくいきませんでした。
エラー: 文字が正しくありません。
コード: 800A03F6
ソース: Microsoft JScript コンパイル エラー
環境変数の設定で迷走した件
(2019/9月始め頃の情報)
当初、下記のようなエラーが出てうまくいかなかったのですが、
Microsoft Windows [Version 10.0.18362.295]
(c) 2019 Microsoft Corporation. All rights reserved.
C:\Users\Owner>cd workspace
C:\Users\Owner\workspace>cd my-app
C:\Users\Owner\workspace\my-app>cd functions
C:\Users\Owner\workspace\my-app\functions>npm run dev
> functions@ dev C:\Users\Owner\workspace\my-app\functions
> nodemon local.js
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! functions@ dev: `nodemon local.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the functions@ dev script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Users\Owner\AppData\Roaming\npm-cache\_logs\2019-09-03T20_36_58_429Z-debug.log
この時は、 GOOGLE_APPLICATION_CREDENTIALS
と FIREBASE_CONFIG
という環境変数両方に、 service-account.json ファイルへのパスを設定してあげるとうまくいきました。
その時参考にしたドキュメント(私の記憶が定かなら内容変わってる気がします…)
GOOGLE_APPLICATION_CREDENTIALS
https://cloud.google.com/docs/authentication/production?hl=ja#obtaining_and_providing_service_account_credentials_manually
FIREBASE_CONFIG
https://firebase.google.com/docs/functions/config-env?hl=ja#automatically_populated_environment_variables
(2019/10/03 最新)
久しぶりにローカルサーバーを起動しようとしてみたら、以前の設定では失敗するようになってまして。
C:\Users\Owner\workspace\my-app\functions>npm run dev
> functions@ dev C:\Users\Owner\workspace\my-app\functions
> nodemon local.js
[nodemon] 1.19.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] starting `node local.js`
undefined:1
C:\Users\Owner\workspace\my-app\functions\service-account.json
^
SyntaxError: Unexpected token C in JSON at position 0
at JSON.parse (<anonymous>)
at Object.setup (C:\Users\Owner\workspace\my-app\functions\node_modules\firebase-functions\lib\setup.js:38:43)
at Object.<anonymous> (C:\Users\Owner\workspace\my-app\functions\node_modules\firebase-functions\lib\index.js:59:9)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Module.require (internal/modules/cjs/loader.js:692:17)
at require (internal/modules/cjs/helpers.js:25:18)
[nodemon] app crashed - waiting for file changes before starting...
環境変数設定されていないときのメッセージも変わっている…どれかのバージョンアップが原因?
(確かにこの間に、Regionを変更するためにプロジェクトを別に立て直したり、ついでにfirebase 関連のいろいろのバージョンを上げたりした)
「Initializing firebase-admin will fail」って言ってるってことは、これを使おうとしてるのは firebase-admin SDK?
C:\Users\Owner\workspace\my-app\functions>npm run dev
> functions@ dev C:\Users\Owner\workspace\my-app\functions
> nodemon local.js
[nodemon] 1.19.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] starting `node local.js`
Warning, FIREBASE_CONFIG and GCLOUD_PROJECT environment variables are missing. Initializing firebase-admin will fail
いろいろ悩んでこのドキュメントを読み直したら、
FIREBASE_CONFIG
https://firebase.google.com/docs/functions/config-env?hl=ja#automatically_populated_environment_variables
process.env.GCLOUD_PROJECT: Firebase プロジェクト ID を提供します
とある。
ので、 GCLOUD_PROJECT
という環境変数にFirebaseのプロジェクトIDを設定してみたら、起動するようになった!
C:\Users\Owner\workspace\my-app\functions>npm run dev
> functions@ dev C:\Users\Owner\workspace\my-app\functions
> nodemon local.js
[nodemon] 1.19.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] starting `node local.js`
Warning, estimating Firebase Config based on GCLOUD_PROJECT. Initializing firebase-admin may fail
FIREBASE_CONFIG
環境変数が設定されていないと上記のWarningが出ますが、起動はします。FIREBASE_CONFIG
にservice-account.jsonのパスを設定すれば、Warningも出なくなります。
そして、どうやら GOOGLE_APPLICATION_CREDENTIALS
環境変数はいらなくなったようです…?
(当初 GCLOUD_PROJECT
を GOOGLE_APPLICATION_CREDENTIALS
に空目して、設定してるはずなんだけどな~…と読み落としていたのでだいぶ悩みました…)
0 件のコメント:
コメントを投稿