Firebase Local Emulator Suite の Functions で FriendlyChat の Vision API や FCM を試してみる

本記事では、Firebase Local Emulator Suite の Functions Emulator を利用してWeb版 FriendlyChat のコードラボにある Vision API(セーフサーチ)や FCM の機能を試してみます。

【目次】

[1]はじめに

本記事は、前の記事『Firebase Local Emulator Suite で Web版 FriendlyChat を動かしてみる』の続編です。

前の記事では、以下の Web版 FriendlyChat を作るコードラボを取り上げました。

このコードラボは、クライアントアプリから Authentication、Firestore、Storage を利用してリアルタイムチャットアプリを作成します。そして、その完成版コードを利用して、Local Emulator Suite で動かしました。

このコードラボには続編があります。本記事はこれを取り上げます。

続編である今回のコードラボでは、Cloud Functions を利用してサーバ側でバックエンドタスクを追加します。

具体的には、Authentication、Firestore、Storage のイベントに反応して、ユーザへ通知したり、不適切な画像が投稿されたらぼかし処理を行う機能を追加します。

本記事では、前回のクライアント側アプリに加えて、Cloud Functions によるサーバ側タスクも Local Emulator Suite で動作させることで、Firebase にデプロイする前に、アプリ全体(クライアントアプリとサーバ処理)をローカル環境で動作テストできることを見ていきたいと思います。

加えて、今回のコードラボで利用する Vision API や Firebase Cloud Messaging(FCM)のように Emulator が提供されていない機能を利用する場合についても触れます。

なお、前回の記事と同様に、コードの説明はコードラボにありますので割愛して、本記事は完成版のソースコードを利用して、Local Emulator Suite で動かすことを目的としています。

なお、Functionsは、無料の Spark プランでは利用できませんので、従量課金制の Blaze プランに切り替える必要があります。ただし、簡単に試すだけなら無料枠に十分収まると思います。

[2]概要

前回の記事と本記事をあわせると少し長くなりますので、全体像を少し整理しておきます。

前回と今回のコードラボを Local Emulator Suite で動かす場合のざっくりとしたイメージ図を書いてみました。

FriendlyChat コードラボと Firebase Emulator Suite


上図のうち、薄緑色のコンポーネントは前回のコードラボで試したものです。
  • Local Emulator Suite を起動すると、プロジェクトディレクトリから以下の内容をロードします。
    • public ディレクトリから Hosting のコンテンツをロードします。
    • firestore.rules の内容を Firestore Emulator に反映します。
    • storage.rules の内容を Storage Emulator に反映します。
  • ブラウザから Hosting Emulator にアクセスすると、FriendlyChat アプリが表示されて、Authentication、Firestore、Storage にアクセスしながら動作します。

これらはクライアント側アプリ(ブラウザで動作するプログラム)の内容です。

今回のコードラボでは、上記の内容に Cloud Functions を利用してサーバ側の処理を追加します。

Local Emulator Suite は Functions の Emulator も実装されており、起動時に(デフォルトでは) functions ディレクトリの内容をロードして実行してくれます。

追加するコンポーネントは、図の薄赤色の部分になります。これにより以下の機能を追加します。
  • 新しいユーザがログインした時、Firebase Bot がウェルカムメッセージを投稿します。
  • 新しい投稿があると、Firebase Cloud Messaging(FCM)を利用して通知します。
    • 前回のコードラボでクライアント側の仕込みは行っていますので、今回のコードラボではFCMを使って自動送信する仕組みを追加します。
  • 不適切な画像を投稿すると、Vision API(セーフサーチ)と Imagemagickを利用して画像をぼかし処理します。

ところで、今回のコードラボの面白いところは、前回のコードラボのコードに変更を加えることなく、サーバ側(Functions)の処理を追加するだけでアプリが機能アップすることです。

今回のコードラボで追加する Functions の処理は、クライアントアプリから直接呼び出すのではなく、Authentication、Firestore、Storage のイベントをトリガーにして動作します。

(実際に、図のブラウザから今回追加する薄赤色のコンポーネントへの矢印がありません。そのかわりに、追加する Functions のコンポーネントが Authentication、Firestore、Storage のイベントを受信する登録を行って、そのイベントの発生がトリガーとなって動作します。)

一方、クライアントアプリは Firestore のデータ変更をリッスンしているので、変化に反応してアプリ画面が更新されます。

結果として、今回のサーバ側の機能追加は、プラグインっぽく(疎結合っぽく)拡張されます。

以上のことから、本記事では前回の記事『Firebase Local Emulator Suite で Web版 FriendlyChat を動かしてみる』で作った環境に、Functions の機能を追加して Local Emulator Suite で動かすための手順を書きます。

また、Vision API とFCMにはエミュレータがありませんので、今回は実際のサービスを利用します。これに関する対処についても書きます。

[3]実行環境の準備

(1)料金プランを従量課金制(Blaze プラン)に移行

Firebase には無料の Spark プランと、従量課金制の Blaze プランがあります。

前回のコードラボで使用した Hosting、Authentication、Firestore、Storage は無料の Spark プランで利用できましたが、今回のコードラボで使用する Functions を利用するためには、料金プランを従量課金制である Blaze プランに移行する必要があります。

Firebase Functions 料金プランアップグレード


従量課金制なので、利用量に応じて課金される可能性がありますが、無料枠もありますので、本記事で取り上げるコードラボを試す程度であれば無料の範囲で試すことができると思います。

従量課金制(Blaze プラン)に移行するには、Firebase コンソールのプロジェクトの画面から Functions の画面を開くと、上の画像のように「プロジェクトをアップグレード」ボタンがありますので、こちらからアップグレードすることができます。

(プロジェクトのホーム画面のプロジェクト名の横に表示されているプラン名(Sparkプラン)をクリックしてもアップグレードすることができます。)

アップグレードが完了すると、Functions の画面は「プロジェクトをアップグレード」ボタンから「使ってみる」ボタンに変わります。

(2)Vision API の有効化

① Vision API のセーフサーチ

FriendlyChat のように、投稿された画像を誰でも見ることができるようなアプリでは、不適切な画像の扱いが重要です。(今回のコードラボの「9. 画像のモデレート」を参照して下さい。)

このため FriendlyChat では、Vision API のセーフサーチを利用して、投稿した画像が不適切な画像であると判断したら自動的に画像にぼかしを施す機能を実装しています。

  • Cloud Vision API:不適切なコンテンツを検出する(セーフサーチ)
  • セーフサーチ検出は、アダルトコンテンツや暴力的コンテンツなど、画像に含まれる不適切なコンテンツを検出します。この機能は 5 つのカテゴリ(adult、spoof、medical、violence、racy)を使用し、それぞれのカテゴリが特定の画像に存在する可能性を返します。
  • 可能性評価は、UNKNOWN、VERY_UNLIKELY、UNLIKELY、POSSIBLE、LIKELY、VERY_LIKELY の 6 つの異なる値で表されます。

何が不適切かどうかはいろいろ難しい問題だと思いますが、このコードラボでは、Vision API のセーフサーチを実行して、アダルトや暴力的コンテンツの可能性が高いもの(LIKELY以上)を不適切と判定しています。

なお、Vision API は画像が不適切かどうかを判定するだけで、画像のぼかし処理は後述の ImageMagick で行います。

② Vision API の有効化手順

Cloud Vision API は、Firebase のコンポーネントではなく、Google Cloud Platform(GCP)が提供する機能です。

このため、Firebase コンソールから Vision API を有効化(利用できるようにすること)はできませんので、Google Cloud コンソールなどを使って Vision API を有効化する必要があります。

なお、Firebase のプロジェクトは GCP のプロジェクトでもありますので、作成した Firebase プロジェクトは GCP のコンソールにも表示されています。

簡単で恐縮ですが、Vision API の有効化手順は以下の通りです。
  1. Firebase と同じ Google アカウントで Cloud Console(統合型 Google Cloud Platform 管理コンソール)にログインします。
  2. FriendlyChat のプロジェクトを選択します。
    • 「プロジェクトの選択」画面の「最近のプロジェクト」タブに表示されていない場合は「すべて」タブを開いて選択します。
  3. Console の左側メニューの「APIとサービス/ライブラリ」を選択して、「Vision API」を検索します。Cloud Vision APIを選択して、「有効にする」ボタンをクリックします。

ちなみに、Vision API は無料のサービスではありませんが、無料枠があります。コードラボを試す程度であれば無料で試すことができると思います。

(参考)
Vision API や Vision API の設定などについては以下の記事も参考にしてください。

(3)ImageMagick のインストール

今回のコードラボでは、Vision API で不適切な画像だと判定した場合、ImageMagick を利用して画像処理(今回の場合はぼかし)を行います。

詳細は割愛しますが、ImageMagickはとても高機能な画像加工ツールです。

Cloud Functions のランタイムには、システムパッケージとして ImageMagick が組み込まれています。(よって Firebase の Functions にも組み込まれています。)

しかし、ローカル環境の Local Emulator Suite で Functions のエミュレータを利用する場合は、ローカル環境に ImageMagick がインストールされている必要があります。

Ubuntu の場合は、以下のコマンドでインストールできます。

sudo apt-get install imagemagick

インストールすると、ターミナルから convert コマンドが利用できるようになります。今回のコードラボのプログラムでは、この convert コマンドを実行して画像処理を行っています。

(参考)
Firebase とは独立して、GCPの Cloud Functions、Google Cloud Vision API、ImageMagick を使用して、Cloud Storage バケットにアップロードされた不適切な画像を検出してぼかす方法のチュートリアルが、Cloud Functions のドキュメントにもあります。

(4)サービスアカウント

FriendlyChat では、新しいメッセージが投稿されたとき、Firebase Cloud Messaging(FCM) を利用して参加者に通知する機能も実装しています。(今回のコードラボの「10. 新しいメッセージ通知」を参照して下さい。)

Vision API と FCM は、 Local Emulator Suite でエミュレータが提供されているわけではありませんので、実際の Firebase や GCP のプロジェクトのサービスにアクセスする必要があります。

デプロイ先の Functions のラインタイムは、これらサービスへのアクセス権が組み込まれて実行されますが、ローカル環境にはアクセス権がありません。

そこで、ここではサービスアカウントの秘密鍵情報をダウンロードして環境変数に設定して実行してみます。

以下の手順の場合、アクセス権に関しては、今回のコードラボのコードに手を加えることなく、Local Emulator Suite で実行する Functions から Vision API や FCM にアクセスできます。(Firebase と GCP の両方に対して有効です。)

<手順>
  1. Firebase コンソールからサービスアカウントの秘密鍵を含むJSONファイルをダウンロードします。
  2. JSONファイルのパスを、環境変数 GOOGLE_APPLICATION_CREDENTIALS に設定します。

export GOOGLE_APPLICATION_CREDENTIALS="KEY_PATH"

ここで KEY_PATH には、ダウンロードしたJSONファイルのパスを指定します。

なお、サービスアカウントを設定した状態で Local Emulator Suite を起動すると、以下のような警告が出ますが、今回の場合は、エミュレータ以外は実際のサービスにアクセスする設定になっていることを確認できます。

 functions: Your GOOGLE_APPLICATION_CREDENTIALS environment variable points to <JSONファイルのパス>. Non-emulated services will access production using these credentials. Be careful!

[4]Functions プログラムの準備

(1)ソースコードとプロジェクトディレクトリの準備

前回のコードラボと同様に、今回のコードラボのソースコードも Github で公開されています。

実はこのページは前回のコードラボと同じです。

前の記事『Firebase Local Emulator Suite で Web版 FriendlyChat を動かしてみる』では、このコードを利用して、Local Emulator Suite で動作するようにソースコードの修正と実行環境を作りました。

本記事では、この環境に今回のコードラボの Functions に関するコードを追加します。

今回のコードラボの完成版ソースコードは、ダウンロードしたディレクトリ「codelab-friendlychat-web」の中の「cloud-functions」ディレクトリです。このうち、今回追加する Functions のコードは「functions」ディレクトリです。

前回の記事の続きで、ホームディレクトリにダウンロードしたディレクトリ(codelab-friendlychat-web)とプロジェクトディレクトリ(friendlychat-web)があれば、以下のように functions ディレクトリをプロジェクトディレクトリにコピーします。

cp -r ~/codelab-friendlychat-web/cloud-functions/functions ~/friendlychat-web

これでプロジェクトディレクトリ(friendlychat-web)の下に functions ディレクトリができて、そこに index.js と package.json の2つのファイルがあると思います。

(参考)
ダウンロードしたディレクトリのうち、今回のコードラボ用のディレクトリは、「cloud-functions-start」と「cloud-functions」です。(cloud-functionsディレクトリは完成形のコードで、-start はコードラボを手順通り進めるためのものです。)

この2つのディレクトリにあるクライアント側のコードは、動作は前回のコードラボで利用した「web」ディレクトリにあるものと同じです。しかし「web」ディレクトリにあるアプリのコードが JS SDK バージョン 9 に対応していたのに対して、「cloud-functions」ディレクトリにあるアプリのコードは、JS SDK バージョン 8 以前のコードのままのようです。

よって、クライアントアプリのコードを含めて「cloud-functions」のディレクトリを利用して Local Emulator Suite を利用する場合は、前回の記事と同様に、エミュレータに接続するコードをクライアントアプリに追加する必要があります。(これは JS SDK バージョン8で記述する必要があります。)

(2)依存パッケージのインストール

以下では functions ディレクトリのコードをみていきますので、カレントディレクトリを functions ディレクトリにして作業します。

cd ~/friendlychat-web/functions

このディレクトリには package.json がありますので確認します。(記事投稿時点)

{
  "name": "friendlychat-codelab",
  "description": "Firebase SDK for Cloud Functions codelab",
  "dependencies": {
    "@google-cloud/vision": "^2.4.0",
    "firebase-admin": "~9.9.0",
    "firebase-functions": "^3.14.1"
  },
  "devDependencies": {
    "eslint": "^7.23.0",
    "eslint-plugin-promise": "^4.3.1"
  },
  "engines": {
    "node": "12"
  },
  "private": true
}

ここでは、engines の node の値が 12 となっています。

インストールしている Node.js のバージョンが 12 と異なる場合、Local Emulator Suite を起動すると以下のような警告が出ます。(下記例は、Node.js バージョン 14 がインストールされている場合です。)

⚠  Your requested "node" version "12" doesn't match your global version "14"

必要に応じて、node の値とインストールしている Node.js のバージョンを合わせておきます。

また、dependencies には、firebase-admin、firebase-functions に加えて Vision API のライブラリも記載されています。

これらの依存パッケージを下記のコマンドでインストールします。

npm install

(補足)
紛らわしいファイル構成になりましたので少し補足します。

プロジェクトディレクトリ「friendlychat-web」は、Firebase にデプロイする構成などを記述した firebase.json をはじめ、クライアントアプリをビルドするための package.json もあり、その依存パッケージは node_modules ディレクトリに格納されます。

そして、functions ディレクトリにも package.json ファイルがあり、その依存パッケージは functions ディレクトリの下の node_modules ディレクトリに格納されます。

紛らわしいですが、この2つのディレクトリにある package.json と node_modules は独立したものです。

ディレクトリ構成の作り方は、いろいろなポリシーがあると思いますので、このような構成が良いかどうかは別として、今回のコードラボは、前回のコードラボのディレクトリに functions ディレクトリを追加する形となっています。

この利点として、firebase.json があるディレクトリの下に functions ディレクトリを作ることで、Local Emulator Suite を実行した時に、自動的に functions ディレクトリを認識して Functions Emulatorを起動してくれることかと思います。

実際の開発においては、スッキリしたディレクトリ構成を考えて、それに応じて(必要なら)firebase.json をカスタマイズして利用するのが良いと思います。

(3)Vision API を利用するための検討

ここからは、Local Emulator Suite で実行する場合に問題となるソースコード(index.js)の内容を見ていきます。

今回のコードラボの「9. 画像のモデレート」で追加している blurOffensiveImages 関数は、Storage に画像が格納されたイベントに応じて、Vision API のセーフサーチを利用して不適切な画像の判定を行っています。

具体的には、Storage の gs:から始めるパス(Google Cloud Storage URI)を Vision API のsafeSearchDetection に渡して判定を行っています。

実際の Storage を利用して動作する場合はこれで問題ないのですが、Local Emulator Suite で動かす場合は問題があります。

問題は、Local Emulator Suite 内に閉じている範囲は Google Cloud Storage URI は正しく動作しますが(実際に、前回のコードラボの内容は意図した通り動作しますが)、Vision API は外部のサービスなので、Local Emulator Suite 内の Google Cloud Storage URI が示すストレージデータにアクセスできません。

このため、何らかの対策を考える必要があります。

詳細は割愛しますが、上記 Vision API の safeSearchDetection の第一引数には、URL以外にも、ローカルファイルパスあるいは画像を格納したBuffer(画像データ)を渡すことができます。

よって、Local Emulator Suite から Vision API を利用する場合は、Emulator の Storage データをローカルファイルか Buffer にダウンロードして呼び出せばよいのですが、後述の通り、Emulator の Storage からダウンロードする場合にも注意事項がありますので、これとあわせて対策を検討します。

(4)Storage Emulator からダウンロードするための検討

アップロードされた画像が Vision API で不適切と判定したら、「blurImage」関数を呼び出します。

この関数で行っている処理は、Storage から該当の画像をローカルのテンポラリファイルにダウンロードして、ImageMagick で画像処理を行い、その結果を Storage にアップロード(上書き)することです。(テンポラリファイルも削除します。)

このうち、ダウンロードのコードは以下のようになっています。

await bucket.file(filePath).download({destination: tempLocalFile});

実際の Storage を利用している場合は問題ないのですが、(記事の投稿時点では)Local Emulator Suite でテンポラリファイルにダウンロードする時に以下のエラーが発生して処理が中断されるようです。

⚠  functions: Error: The downloaded data did not match the data from the server. To be sure the content is the same, you should download the file again.
⚠  Your function was killed because it raised an unhandled error.

理由をネットで探してみると、以下のIssueを見つけました。

これを参考に、download メソッドのオプションに validation: false を追加して試してみるとエラー無くダウンロードできました。

(参考)
Google Cloud Storage: Node.js Client の download とオプションの詳細は以下を参考にしてください。

(5)Vision API と Storage Emulator に対応するコード例

上記 Vision API 呼び出しの問題と Emulator の Storage からのダウンロードの問題に対応するコードを検討します。

といっても、本格的に対応するならコードの構造から考えたくなりますが、本記事は今回のコードラボのコードを動かすことですので、以下の方針で考えることにします。

  • コードラボのコードに極力手を入れないようにする。
  • Firebase プロジェクトにデプロイした時はコードラボのコードの通りに動作する。
    • Local Emulator Suite で実行する場合のみ必要な処理を追加する。
  • デプロイした環境かローカル環境かの区別は、簡単のため環境変数RUN_LOCAL_EMULATOR の有無で識別する。
    • ちなみに、前の記事で Local Emulator Suite を利用するかどうかはローカルホストで実行しているかどうかで切り替えるコード例を書きましたが、Functions はブラウザで動作するわけではありませんので、この方法は使えません。

これらを鑑みた blurOffensiveImages と blurImage 関数の変更コード例です。
変更箇所は、変更:ここから~ 変更:ここまで、とある2箇所です。

// Checks if uploaded images are flagged as Adult or Violence and if so blurs them.
exports.blurOffensiveImages = functions.runWith({memory: '2GB'}).storage.object().onFinalize(
    async (object) => {
      // =====> 変更:ここから
      //const imageUri = `gs://${object.bucket}/${object.name}`;
      // 環境変数 RUN_LOCAL_EMULATOR が設定されている場合、Local Emulator Suiteで実行しているとする。
      let imageUri;
      if (process.env.RUN_LOCAL_EMULATOR != undefined) {
        // Local Emulator Suiteで実行している場合は、画像イメージをBufferへダウンロードして、
        // imageUriに設定する。(Vision APIには画像データを送信する)
        const bucket = admin.storage().bucket();
        // Local Emulator Suiteで実行している場合は、downloadのオプションに
        // validation:false を追加しておく。
        const contents = await bucket.file(object.name).download({validation: false});
        imageUri = contents[0];
        functions.logger.log('The Image',object.name,' has been downloaded to buffer.');
      } else {
        // それ以外はStorageのパスを指定(もとのまま)
        imageUri = `gs://${object.bucket}/${object.name}`;
      }
      // <===== 変更:ここまで

      // Check the image content using the Cloud Vision API.
      const batchAnnotateImagesResponse = await vision.safeSearchDetection(imageUri);
      const safeSearchResult = batchAnnotateImagesResponse[0].safeSearchAnnotation;
      const Likelihood = Vision.protos.google.cloud.vision.v1.Likelihood;
      if (Likelihood[safeSearchResult.adult] >= Likelihood.LIKELY ||
          Likelihood[safeSearchResult.violence] >= Likelihood.LIKELY) {
        functions.logger.log('The image', object.name, 'has been detected as inappropriate.');
        return blurImage(object.name);
      }
      functions.logger.log('The image', object.name, 'has been detected as OK.');
    });

// Blurs the given image located in the given bucket using ImageMagick.
async function blurImage(filePath) {
  const tempLocalFile = path.join(os.tmpdir(), path.basename(filePath));
  const messageId = filePath.split(path.sep)[1];
  const bucket = admin.storage().bucket();

  // Download file from bucket.
  // =====> 変更:ここから
  // await bucket.file(filePath).download({destination: tempLocalFile});
  // 環境変数 RUN_LOCAL_EMULATOR が設定されている場合、Local Emulator Suiteで実行しているとする。
  let dl_options;
  if (process.env.RUN_LOCAL_EMULATOR != undefined) {
    // Local Emulator Suiteで実行している場合は、downloadのオプションに
    // validation:false を追加しておく。
    dl_options = {destination: tempLocalFile, validation: false};
  } else {
    // それ以外はもとのまま
    dl_options = {destination: tempLocalFile};
  }
  await bucket.file(filePath).download(dl_options);
  // <===== 変更:ここまで

  functions.logger.log('Image has been downloaded to', tempLocalFile);
  // Blur the image using ImageMagick.
  await exec(`convert "${tempLocalFile}" -channel RGBA -blur 0x24 "${tempLocalFile}"`);
  functions.logger.log('Image has been blurred');
  // Uploading the Blurred image back into the bucket.
  await bucket.upload(tempLocalFile, {destination: filePath});
  functions.logger.log('Blurred image has been uploaded to', filePath);
  // Deleting the local file to free up disk space.
  fs.unlinkSync(tempLocalFile);
  functions.logger.log('Deleted local file.');
  // Indicate that the message has been moderated.
  await admin.firestore().collection('messages').doc(messageId).update({moderated: true});
  functions.logger.log('Marked the image as moderated in the database.');
}

少し補足しておきます。
  • 環境変数 RUN_LOCAL_EMULATOR が定義されていたら、Local Emulator Suite で実行していると判断して対応コードを実行します。
  • blurOffensiveImages と blurImage に download メソッドを利用していますが、オプションに validation: false を指定しています。
  • blurOffensiveImages では、元コードの safeSearchDetection 呼び出しの引数 imageUri をデプロイ環境とローカル環境でわけています。
    • ローカル実行の場合は Emulator の Storage から画像データを Buffer にダウンロードして、Buffer を imageUri として Vision API を呼び出しています。
    • ここで Storage から Buffer にデータをダウンロードする方法はいろいろ考えられますが、ここではコードが短いという単純な理由で以下のサイトの情報を参考にしました。

(6)ロケーションの変更コード

前の記事で Firebase のプロジェクトの Firestore(または Storage)の設定を行う際にロケーションの設定を行ったと思います(GCPのデフォルトロケーション)。

このロケーション設定とは別に、Functions では実行するリージョンを指定します。

Local Emulator Suite での実行には関係ありませんが、本番環境で実行する場合は検討しておくべき内容かと思います。

具体的には、リージョン指定をせず、今回のコードラボのコードのまま Local Emulator Suite で起動すると、起動ログに以下のように表示されます。

✔  functions[us-central1-addWelcomeMessages]: auth function initialized.
✔  functions[us-central1-blurOffensiveImages]: storage function initialized.
✔  functions[us-central1-sendNotifications]: firestore function initialized.

リージョンが us-central1 になっています。

Functions のリージョン指定は、Firebase コンソールで設定するのではなく、コードで指定します。

例えば、リージョンを東京に設定する場合は、関数毎に「.region('asia-northeast1')」を追加します。

// addWelcomeMessages のリージョン変更
exports.addWelcomeMessages = functions.region('asia-northeast1').auth.user().onCreate(async (user) => {
~以下省略~

// blurOffensiveImages のリージョン変更
exports.blurOffensiveImages = functions.region('asia-northeast1').runWith({memory: '2GB'}).storage.object().onFinalize(
~以下省略~

// sendNotifications のリージョン変更
exports.sendNotifications = functions.region('asia-northeast1').firestore.document('messages/{messageId}').onCreate(
~以下省略~

すると Local Emulator Suite の起動表示が以下のように変わります。

✔  functions[asia-northeast1-addWelcomeMessages]: auth function initialized.
✔  functions[asia-northeast1-blurOffensiveImages]: storage function initialized.
✔  functions[asia-northeast1-sendNotifications]: firestore function initialized.

[5]FriendlyChat を Local Emulator Suite で実行

(1)起動方法

環境とコードの準備ができたところで、Local Emulator Suite で動かしてみます。

まずは、カレントディレクトリをプロジェクトディレクトリ(firebase.json ファイルがあるディレクトリ)にします。

cd ~/friendlychat-web

Vision API と Emulator の Storage からのダウンロードに対処したコードが実行されるように、環境変数を設定します。

export RUN_LOCAL_EMULATOR=""

Vision API と FCM にアクセスできるようにサービスアカウントを秘密鍵情報のJSONファイルのパスを環境変数に設定します。

export GOOGLE_APPLICATION_CREDENTIALS="KEY_PATH"

Local Emulator Suite を起動します。

firebase emulators:start

functions ディレクトリがあれば Functions のエミュレータも起動していると思います。(起動ログや Emulator UI で確認できます。)
もし Functions のエミュレータが起動していない場合は、以下のコマンドで確認、設定します。

firebase init emulators

(2)動作確認

前の記事と同じ手順で、Local Emulator Suite の Hosting で動作する FriendlyChat アプリにアクセスします。

Cloud Functions で機能追加した FriendlyChat


前回に比べて新しい動作が追加されています。
  • 新しいアカウントでログインした時に、Firebase Bot がウェルカムメッセージを投稿してくれます。
  • 不適切な画像を投稿するとぼかし処理されます。
    • 投稿した時点ではそのままの画像が表示され、不適切な場合は、ぼかし処理して画像が置き換えられます。
    • 上記の元画像は今回のコードラボにあるサンプルのものですが、ブログに掲載することと、何より自分でも見たくないので(涙)、ぼかしを強くしています。
  • 新しい投稿があると、FCMによる通知が届いています。
    • 前回のコードラボでは Firebase コンソールのテスト通知で動作を確認していましたが、今回は新しい投稿をトリガーにして自動送信されます。

[6]Firebase にデプロイして実行

実際に Firebase にデプロイして動作を確認してみます。

カレントディレクトリがプロジェクトディレクトリ(firebase.json ファイルがあるディレクトリ)であることを確認します。

cd ~/friendlychat-web

以下のコマンドでデプロイします。

firebase deploy --only functions

ちなみに、Functions だけをデプロイする場合は、--only functions フラグを指定しますが、これまでのコードラボ全体をデプロイする場合は、--only functions フラグは不要です。

デプロイが完了したら、Firebase コンソールにログインしてプロジェクトの画面を開きます。
  • Hosting 画面
    • 「ドメイン」に表示されている URL をクリックすると、FriendlyChat アプリが表示されますので、動作確認します。
  • Functions 画面
    • デプロイした3つの関数が表示されていると思います。この一覧でリージョンやランタイムなども確認できます。
    • 別のタブでログや使用状況も確認できます。

最後に、不要になったら(従量課金プランにアップグレードしていますので)プロジェクトを削除しておくと安心です。

コメント

このブログの人気の投稿

VirtualBoxのスナップショット機能

Google Document AIで画像から表形式データを抽出する(Vision API OCRとの違い)

Ubuntu/Colab環境でPDFファイルのページを画像化する(pdf2image、pdftoppm、pdftocairo)