firebase emulatorの設定と使用

# 概要
エミュレータの設定と、エミュレータを使った開発メモ

# emulatorの設定

こちらに書いてあるので、詳細を説明するまでもありませんが、一応ポイントのみ解説
https://firebase.google.com/docs/emulator-suite/install_and_configure?hl=ja

## 予めインストールしておくもの

- node (当たり前ですが)
- Firebase CLI (`npm i -g firebase-tools`)
- JDK 1.8以降

## インストール

作業ディレクトリ (firebase initしてあるディレクトリ) で以下を実行

```console
$ firebase init emulators
```

実行すると、どのエミュレータを設定するか聞かれるので、必要なものを選択する。
次にポート番号を求められるので特に別で使用しているポートなどがなければデフォルトのまま進む。
最後にエミュレータをダウンロードするか聞かれるのでYで進める
`? Would you like to download the emulators now? Yes`

```console

     ######## #### ########  ######## ########     ###     ######  ########
     ##        ##  ##     ## ##       ##     ##  ##   ##  ##       ##
     ######    ##  ########  ######   ########  #########  ######  ######
     ##        ##  ##    ##  ##       ##     ## ##     ##       ## ##
     ##       #### ##     ## ######## ########  ##     ##  ######  ########

You're about to initialize a Firebase project in this directory:

  /Users/path/directory

Before we get started, keep in mind:

  * You are initializing in an existing Firebase project directory


=== Project Setup

First, let's associate this project directory with a Firebase project.
You can create multiple project aliases by running firebase use --add, 
but for now we'll just set up a default project.

i  .firebaserc already has a default project, using <project id>.

=== Emulators Setup
? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices. (Press <space> to select, <
a> to toggle all, <i> to invert selection)Authentication Emulator, Functions Emulator, Firestore Emulator, Pub/Sub Emulator
i  Port for auth already configured: 9099
i  Port for functions already configured: 5001
i  Port for firestore already configured: 8080
i  Port for pubsub already configured: 8085
i  Emulator UI already enabled with port: (automatic)
? Would you like to download the emulators now? Yes
i  ui: downloading ui-v1.4.2.zip...

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!
```

成功したら、emulatorを開始する

```
$ firebase emulators:start
```

開始するとエミュレータのインストールが実行され、その後エミュレータが起動する。

```console

functions % firebase emulators:start
i  emulators: Starting emulators: auth, functions, firestore, pubsub
⚠  functions: The following emulators are not running, calls to these services from the Functions emulator will affect production: database, hosting
✔  functions: Using node@10 from host.
i  firestore: Firestore Emulator logging to firestore-debug.log
i  pubsub: Pub/Sub Emulator logging to pubsub-debug.log
i  ui: downloading ui-v1.4.2.zip...
Progress: =========================================================================================================================> (100% of 4MB
i  ui: Emulator UI logging to ui-debug.log
...
```

その後、functionsを使用している場合はエミュレータ上にfunctionがデプロイされる。
emulatorの準備が終わると以下のような表示になり、 `localhost:4000` を叩くとemulator画面が表示される。

```
┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000                │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4000/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Functions      │ localhost:5001 │ http://localhost:4000/functions │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ localhost:8080 │ http://localhost:4000/firestore │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Pub/Sub        │ localhost:8085 │ n/a                             │
└────────────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://githu
```

emulator UIにアクセスした状態

![スクリーンショット 2021-03-24 11.05.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252067/cd707543-d4ba-3546-8508-f11202d22d79.png)

起動できたら `control + C`で 一度終了する

# emulatorへのアクセス

authでログイン、firestoreへの読み取りアクセス、functionの実行を行う。

## auth emulator へのユーザ追加
ログイン用のユーザを追加する。
プログラム側で追加できるが、emulator UIを使ってユーザを追加できる。
emulator UI にアクセスし（ポートを変更していなければlocalhost:4000)、Authenticationの画面でユーザを追加できる

![スクリーンショット 2021-03-30 11.09.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252067/a08a2cd9-db52-c975-4216-6e9635f2cdeb.png)

custom claimsも設定できるので便利。

## クライアントからの呼び出し

node.jsから呼び出しを行う。

firebaseをinitializeAppした後、それぞれuseEmulator()をしてあげれば良い。
functionのregionも通常通り指定でOK。


```js
const firebase = require("firebase")

  const firebaseConfig = {
    apiKey: "xxxxxxxxxxxx",
    authDomain: "test.service.local",
    databaseURL: "xxxxxxxxxxx",
    projectId: "test-service",
    storageBucket: "test-service.appspot.com",
    messagingSenderId: "000000000",
    appId: "xxxxxxxxxxxxxxxxxxxxx"
  };

firebase.initializeApp(firebaseConfig);

let auth = firebase.default.auth();
let functions = firebase.default.app().functions("asia-northeast1");
let db = firebase.default.firestore();

//emulator設定
auth.useEmulator("http://localhost:9099");
functions.useEmulator("localhost", 5001);
db.useEmulator("localhost", 8080);


(async () => {

  try {

    //auth emulatorへのアクセス（ログイン)
    await auth.signInWithEmailAndPassword("test@email.local", "password");

    //firestore emulatorへのアクセス
    const snapshot = await db.collection("test").get();
    snapshot.docs.map(doc => console.log(doc.data().name));
    
    //function emulatorへのアクセス
    const func = await functions.httpsCallable("testFunction")({});
    console.log(func.data);

  } catch (e) {
    console.log(e);
  }

})();


```

# 本番のfirestoreデータをemulator用に利用する

本番のfirestoreに既にデータが入っていてそれをエミュレータ上で利用したい場合がある。
そんな時はデータを予め本番環境からエクスポートしておく必要がある。
手順が若干面倒なのでこれも念の為メモ。

## firestoreのエクスポート

firestoreデータのエクスポートは以下のGCPコンソールから実行できる。
必要なコレクションのみチェックしてエクスポートしておく

<https://console.cloud.google.com/firestore/import-export?hl=ja>

## ローカルディスクへのダウンロード

エクスポートはstorage上にしか保存できいないため、さらにそれをローカルディスクに保存するにはgsutilツールを利用しなければならない。
以下を予めインストールしておく

＊ gsutilのインストール
https://cloud.google.com/storage/docs/gsutil_install?hl=ja

以下のコマンドを実行してダウンロードする

```console
$ gsutil -m cp -r gs://<エクスポート先のstorageアドレス> <保存先のパス>
```

## コピーしたデータを利用してemulatorを開始する

ローカルディスクに保存したデータをfirestoreのデータとして読み込んで起動するには、`--import` オプションを使用する

以下のようにコピーしたデータへのパスをオプション引数に渡して実行する。この時、パスは「xxxx.overall_export_metadata」がルートとなるように指定する。

```console
$ firebase emulators:start --only firestore --import=<ダウンロード保存したディレクトリへのパス>
```

firestore emulatorの画面を見るとデータがインポートされていることが確認できる

![スクリーンショット 2021-03-24 14.18.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252067/88745b10-bf22-286e-e841-7be11792a05b.png)

## emulator上で追加したデータをエクスポート

emulatorを使ってデータを追加した場合、emulatorを終了すると基本全てクリアされる。
そのためauthなどを使うテストの場合いちいち再作成しなければならない。
その場合、終了時に追加したデータをエクスポートするオプション`--export-on-exit`をつけると良い。

```
$ firebase emulators:start --export-on-exit=<エクスポート先>
```