2019年11月23日土曜日

NuxtでFirestoreを利用してデータを保存する

Firestoreからデータを取得して表示するサンプルを作成します。
Jest でテストもします。

完成したプロジェクトは以下のリポジトリになります。
https://github.com/TAC/nuxt-firestore-example

Firestoreに接続する

まずはFirestoreへ接続します。
Firebaseのアカウント作成などは他のサイトなどで確認して下さい。
接続情報は.env/development.jsに記載して、nuxt.config.tsで読み込むようにします。

.env/development.js

module.exports = {
  apiKey: "<your apiKey>",
  authDomain: "<your authDomain>",
  databaseURL: "<your databaseURL>",
  projectId: "<your projectId>",
  storageBucket: "<your storageBucket>",
  messagingSenderId: "<your api messagingSenderId>"
}

app/nuxt.config.ts

import NuxtConfiguration from '@nuxt/config'
import pkg from './package.json'
import envSet from '../.env/environment.js'

const nuxtConfig: NuxtConfiguration = {
  mode: 'universal',
  srcDir: 'src',

  env: envSet,
  ...
}

設定値はprocess.env.apiKeyという形式で利用できます。
次にFirebaseFirestore の初期設定を行うプラグインを作成します。

app/src/plugins/firebase.ts

import firebase from 'firebase/app'

if (!firebase.apps.length) {
  firebase.initializeApp({
    apiKey: process.env.apiKey,
    authDomain: process.env.authDomain,
    databaseURL: process.env.databaseURL,
    projectId: process.env.projectId,
    storageBucket: process.env.storageBucket,
    messagingSenderId: process.env.messagingSenderId
  })
}

export default firebase

ここまでは 前回のAuthenticationの実装 でやりました。
Firestore へ接続するプラグインは以下のように記述します。

app/src/plugins/firestore.ts

import firebase from '@/plugins/firebase'
import 'firebase/firestore'

const db = firebase.firestore()

export default db

修正箇所

ユーザのニックネームを Firestore へ保存します。
users コレクションを作成して、ドキュメントIDには AuthenticationユーザーUID を利用します。
認証データをそのまま保存していた store/models/users.ts を変更していきます。

store/models/users.ts

import {
  VuexModule,
  Module,
  getter,
  mutation,
  action,
  getRawActionContext
} from 'vuex-class-component'
import firebase from '@/plugins/firebase'
import db from '@/plugins/firestore'
import Users from '@/types/store/models/users'

const timestamp = firebase.firestore.FieldValue.serverTimestamp()

@Module({ namespacedPath: 'models/users/', target: 'nuxt' })
class Store extends VuexModule {
  @getter user: Users = {}

  get isAuthenticated() {
    return !!this.user.id
  }

  @mutation
  public SET_USER(payload: Users) {
    this.user = payload
  }

  @mutation
  public UNSET_USER() {
    this.user = {}
  }

  @action({ mode: 'raw' })
  public async set(uid: string) {
    if (uid) {
      const ref = db.collection('users').doc(uid)
      const doc = await ref.get()
      if (doc.exists) {
        const data: Users = {
          id: doc.id,
          nickName: doc.data()!.nickName,
          createdAt: doc.data()!.createdAt,
          updatedAt: doc.data()!.updatedAt
        }
        const context = getRawActionContext(this)
        context.commit('SET_USER', data)
      }
    }
  }

  @action({ mode: 'raw' })
  public async unset() {
    const context = getRawActionContext(this)
    context.commit('UNSET_USER')
  }

  @action({ mode: 'raw' })
  public async create(uid: string) {
    if (uid) {
      const ref = db.collection('users').doc(uid)
      const doc = await ref.get()
      if (!doc.exists) {
        const nickName = ''
        await ref.set({
          nickName,
          createdAt: timestamp,
          updatedAt: timestamp
        })

        const data: Users = {
          id: uid,
          nickName
        }
        const context = getRawActionContext(this)
        context.commit('SET_USER', data)
      }
    }
  }

  @action({ mode: 'raw' })
  public async update(user: Users) {
    const ref = db.collection('users').doc(user.id)
    const doc = await ref.get()
    if (doc.exists) {
      await ref.update({
        nickName: user.nickName,
        updatedAt: timestamp
      })
      const context = getRawActionContext(this)
      context.commit('SET_USER', user)
    }
  }
}

export default Store.ExtractVuexModule(Store)

以前は mutation しかありませんでしたが、 action を追加して、そこでユーザデータの登録・作成を行うようにしています。

認証のサインイン時の処理も修正します。

store/modules/auth.ts

  @action({ mode: 'raw' })
  public async signIn(provider) {
    const context = getRawActionContext(this)
    context.commit('models/users/UNSET_USER', null, { root: true })
    await firebase
      .auth()
      .signInWithPopup(provider)
      .then(result => {
        if (result && result.user && result.additionalUserInfo) {
          if (result.additionalUserInfo.isNewUser) {
            // new user
            context.dispatch('models/users/create', result.user.uid, {
              root: true
            })
          } else {
            // exists user
            context.dispatch('models/users/set', result.user.uid, {
              root: true
            })
          }
        }
      })
      .catch(error => {
        console.error(error)
        throw error
      })
  }

result.additionalUserInfo.isNewUser をみて新規ユーザかどうかを判定して、users コレクションへの登録処理を実行しています。
一度認証してしまっていると上記の値が false になってしまうので、動作確認する場合は新しいアカウントか Firebase Console で認証情報を削除すると users コレクションが登録されると思います。

テストの作成

認証処理の時にも使った firebase-mock を使ってテストケースを作っていきます。

app/tests/store/models/users.test.js

import db from '@/plugins/firestore'
...
describe('store/models/users.ts', () => {
  beforeAll(() => {
    db.autoFlush()
  })

  beforeEach(async () => {
    // mock data
    user = {
      nickName: 'alice',
      createdAt: '2019-10-26 00:00:00',
      updatedAt: '2019-10-26 00:00:00'
    }

    await db
      .collection('users')
      .doc('alice')
      .set(user)
  })

plugins/firestoreimport を追加してます。
前回の設定firestore の呼び出しはモックになっています。
beforeAll でFirestoreへの処理がすぐ反映されるように autoFlush() を実行しておきます。
beforeEach でモックデータを登録しています。
モックなので実際にFirestoreには登録されません。

app/tests/store/models/users.test.js

  describe('actions', () => {
    test('set', async () => {
      await store.dispatch('models/users/set', 'alice')
      expect(store.getters['models/users/user']).toMatchObject(user)
    })

    test('unset', async () => {
      await store.dispatch('models/users/unset')
      expect(store.getters['models/users/user']).toMatchObject({})
    })

    test('create', async () => {
      await store.dispatch('models/users/create', 'bob')
      const checkData = {
        id: 'bob',
        nickName: ''
      }
      expect(store.getters['models/users/user']).toMatchObject(checkData)
    })

    test('update', async () => {
      const updateData = {
        id: 'alice',
        nickName: 'test'
      }
      await store.dispatch('models/users/update', updateData)
      const data = store.getters['models/users/user']
      expect(data).toMatchObject(updateData)
      expect(data.updatedAt).not.toBe(user.updatedAt)
    })
  })

追加した action メソッドのテストを作成します。
なお、gettersmutation は動作を変えていないので、修正しなくても正常にテストを通過するはずです。

以上、簡単にですが Firestore への接続とテスト方法の紹介でした!


Written with StackEdit.

2019年11月14日木曜日

firebase-tools v7.6.0 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.6.0

  • Added support for managing the Realtime Database setting strictTriggerValidation.
  • Fixes trigger parser to not rely on prototype methods (#1687).
  • Fixes bug where standalone CLI would hang in the Android Studio integrated terminal.
  • Fixes a bug where accessing refs from background function arguments would point to prod (#1682).
  • Fixes a bug where WSL users without java would get an uncaught exception (#1713).

  • Realtime Database の設定 strictTriggerValidation の管理のサポートが追加されました。
  • トリガーパーサーがプロトタイプメソッドに依存しないように修正しました。(#1687)
  • スタンドアロンCLIがAndroid Studio統合端末でハングするバグを修正しました。
  • バックグラウンド関数の引数から refs にアクセスすると prod を指すバグを修正しました。(#1682)
  • Javaを使用しないWSLユーザーがキャッチされない例外を受け取るバグを修正しました。(#1713)

Written with StackEdit.

2019年11月11日月曜日

firebase-tools v7.5.0 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.5.0

  • Firestore Emulator now serves WebChannel traffic on the same port as gRPC.
  • Fix bug where emulators could not find free ports on Windows Subsystem for Linux.
  • Fixes invalid JSON output in auth.export within a scripting environment.

  • Firestore Emulatorは、gRPCと同じポートでWebChannelトラフィックを処理するようになりました。
  • エミュレーターが Windows Subsystem for Linux で空きポートを見つけられなかったバグを修正しました。
  • スクリプト環境内の auth.export での無効なJSON出力を修正しました。

Written with StackEdit.

2019年11月6日水曜日

firebase-tools v7.4.0 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.4.0

  • Initial release of the appdistribution:distribute command. Refer to the Firebase App Distribution Firebase CLI documentation for Android and iOS for more information about using the Firebase CLI to quickly distribute pre-release versions of your apps to testers.
  • Initial release of the Firebase Extensions ext:* commands. Refer to the CLI documentation for more information about the available commands.
  • (Removed some features from the preview list.)

  • appdistribution:distribute コマンドの初期リリース。Firebase CLIを使用してアプリのプレリリースバージョンをテスターにすばやく配布する方法の詳細については、Firebase App Distribution Firebase CLI documentation for Android および iOS を参照してください。
  • Firebase Extensions ext:*コマンドの初期リリース。 使用可能なコマンドの詳細については、CLIドキュメントを参照してください。
  • (プレビューリストから一部の機能を削除しました。)

Written with StackEdit.

2019年10月19日土曜日

firebase-tools v7.3.2 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.3.2

  • Ensures auth:export results are fully flushed to the output file.
  • Fix bug in Firestore emulator where concurrent requests for the same transaction would sometimes hang.
  • Fix bug in Firestore emulator where WriteResults for deletes had an update_time populated.
  • Set the predefined runtime environment variables in the functions emulator

  • auth:export の結果が出力ファイルに完全にフラッシュされるようにします。
  • 同じトランザクションの同時リクエストが時々ハングするFirestoreエミュレータのバグを修正しました。
  • 削除のWriteResultsに update_time が入力されていたFirestoreエミュレータのバグを修正しました。
  • Functionsエミュレータで定義済みのランタイム環境変数を設定します。

Written with StackEdit.

2019年10月18日金曜日

firebase-tools v7.3.1 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.3.1

  • Fixed bug in Firestore emulator where transactions that timed out could still modify data.
  • Fixed bug where the auth:export command could export the same page of users multiple times.

  • タイムアウトしたトランザクションがデータを変更する可能性があるFirestoreエミュレータのバグを修正しました。
  • auth:export コマンドが同じページのユーザーを複数回エクスポートできるバグを修正しました。

Written with StackEdit.

2019年10月17日木曜日

firebase-tools v7.3.0 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.3.0

  • Mark list command as deprecated.
  • Mark setup:web command as deprecated.
  • Mark tools:migrate command as deprecated.
  • Fix bug in Cloud Firestore emulator where committing a transaction with no writes would not release locks.

  • list コマンドを非推奨としてマークします。
  • setup:web コマンドを非推奨としてマークします。
  • tools:migrate コマンドを非推奨としてマークします。
  • 書き込みのないトランザクションをコミットしてもロックが解除されない Cloud Firestore エミュレーターのバグを修正しました。

Written with StackEdit.

2019年10月16日水曜日

firebase-tools v7.2.4 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.2.4

  • Update portfinder dependency to avoid breakage when installing firebase-tools.

  • firebase-toolsをインストールする際の破損を避けるためにportfinderの依存関係を更新しました。

Written with StackEdit.

2019年10月15日火曜日

firebase-tools v7.2.3 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.2.3

  • Introduce experimental support for browser clients to the Firestore emulator.
  • No longer require authentication to run the functions emulator.
  • Add new project management commands: projects:create, projects:list, projects:addFirebase.
  • Add new app management commands: apps:create, apps:list, apps:sdkconfig.
  • Improve init command to be able to create a new project.
  • Automatically choose a port for the Firestore emulator to serve WebChannel traffic.
  • Add support for running queries inside of transactions to the Firestore emulator.

  • ブラウザクライアントの実験的なサポートをFirestoreエミュレータに導入しました。
  • Functions エミュレーターを実行するために認証が不要になりました。
  • 新しいプロジェクト管理コマンドを追加: projects:create, projects:list, projects:addFirebase
  • 新しいアプリ管理コマンドを追加: apps:create, apps:list, apps:sdkconfig
  • init コマンドを改善して、新しいプロジェクトを作成できるようにします。
  • Firestoreエミュレーターのポートを自動的に選択して、WebChannelトラフィックを処理します。
  • トランザクション内でクエリを実行するためのサポートをFirestoreエミュレータに追加しました。

Written with StackEdit.

2019年10月14日月曜日

firebase-tools v7.2.2 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.2.2

  • Allow passing GOOGLE_APPLICATION_CREDENTIALS environment variable into the functions emulator.
  • Set FIREBASE_DATABASE_EMULATOR_HOST in emulators:exec.
  • Add upsert API for function triggers to the database emulator.
  • Fix a bug where only one RTDB function could be registered by using the RTDB emulator upsert API to register functions triggers.

  • GOOGLE_APPLICATION_CREDENTIALS 環境変数を Functions エミュレーターに渡すことを許可します。
  • emulators:execでFIREBASE_DATABASE_EMULATOR_HOSTを設定します。
  • Functions トリガーの upsert APIをデータベースエミュレーターに追加します。
  • RTDBエミュレータ upsert APIを使用して Functions トリガーを登録することにより、1つのRTDB関数のみを登録できるバグを修正しました。

Written with StackEdit.

2019年10月9日水曜日

Nuxt.js v2.8.1 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/nuxt/nuxt.js/releases/tag/v2.8.1

🐛 Bug Fixes

バグ修正

  • vue-renderer
    • #5867 Fix exception on property access of undefined object
    • #5863 Prevent “can’t set headers after they are sent” error in modern server mode
  • vue-app
  • #5864 Reduce consola direct access and don’t override browser console.log
  • cli
    • #5865 Don’t mutate options export to prevent ESM regression with nuxt.config in mixed (cjs + esm) mode (TIP: never mix them as is non-standard and may be unsupported in any future version)

  • vue-renderer
    • #5867 未定義オブジェクトのプロパティアクセスの例外を修正
    • #5863 最新のサーバーモードで「送信後にヘッダーを設定できない」エラーを防止
  • vue-app
    • #5864 consolaの直接アクセスを減らし、ブラウザーの console.log をオーバーライドしないようにした
  • cli
    • #5865 mixed (cjs + esm) モードで nuxt.config を使用したESM回帰を防止するためにオプションのエクスポートを変更しないでください (TIP: 非標準であるため、これらを混合しないでください。将来のバージョンではサポートされない可能性があります)

💅 Refactors

リファクタ

  • typescript
    • #5854 Prepare for external typescript support

  • typescript
    • #5854 外部 Typescript サポートを準備する

Written with StackEdit.

2019年9月29日日曜日

Nuxtでcore-jsのエラーの解消

以前、こちらの記事
以下のエラーに対応するために core-js@2.6.7 を別途追加する
旨の記載をしましたが、対応が間違っていたようです。

こちらのリポジトリを修正してあります。
https://github.com/TAC/nuxt-firebase-auth-example

These dependencies were not found:                                                                                                                                      friendly-errors 16:19:36
                                                                                                                                                                        friendly-errors 16:19:36
* core-js/modules/es6.array.find in ./.nuxt/client.js                                                                                                                   friendly-errors 16:19:36
* core-js/modules/es6.array.iterator in ./.nuxt/client.js                                                                                                               friendly-errors 16:19:36
* core-js/modules/es6.date.to-string in ./.nuxt/utils.js, ./src/helpers/cookies.ts                                                                                      friendly-errors 16:19:36
* core-js/modules/es6.function.name in ./.nuxt/client.js, ./src/store/models/users.ts                                                                                   friendly-errors 16:19:36
* core-js/modules/es6.object.assign in ./.nuxt/client.js                                                                                                                friendly-errors 16:19:36
* core-js/modules/es6.object.keys in ./.nuxt/index.js                                                                                                                   friendly-errors 16:19:36
* core-js/modules/es6.object.to-string in ./.nuxt/router.scrollBehavior.js, ./.nuxt/components/nuxt-link.client.js and 2 others                                         friendly-errors 16:19:36
* core-js/modules/es6.promise in ./.nuxt/client.js                                                                                                                      friendly-errors 16:19:36
* core-js/modules/es6.regexp.constructor in ./.nuxt/utils.js                                                                                                            friendly-errors 16:19:36
* core-js/modules/es6.regexp.match in ./.nuxt/client.js                                                                                                                 friendly-errors 16:19:36
* core-js/modules/es6.regexp.replace in ./.nuxt/store.js, ./.nuxt/components/nuxt.js                                                                                    friendly-errors 16:19:36
* core-js/modules/es6.regexp.search in ./.nuxt/utils.js                                                                                                                 friendly-errors 16:19:36
* core-js/modules/es6.regexp.split in ./.nuxt/store.js, ./node_modules/babel-loader/lib??ref--2-0!./node_modules/vue-loader/lib??vue-loader-options!./.nuxt/components/nuxt-build-indicator.vue?vue&type=script&lang=js&
* core-js/modules/es6.regexp.to-string in ./.nuxt/utils.js, ./src/helpers/cookies.ts                                                                                    friendly-errors 16:19:36
* core-js/modules/es6.string.includes in ./.nuxt/client.js, ./.nuxt/components/nuxt-link.client.js and 1 other                                                          friendly-errors 16:19:36
* core-js/modules/es6.string.iterator in ./.nuxt/App.js                                                                                                                 friendly-errors 16:19:36
* core-js/modules/es6.string.repeat in ./.nuxt/utils.js                                                                                                                 friendly-errors 16:19:36
* core-js/modules/es6.string.starts-with in ./.nuxt/utils.js, ./src/helpers/cookies.ts                                                                                  friendly-errors 16:19:36
* core-js/modules/es6.symbol in ./.nuxt/store.js, ./.nuxt/components/nuxt-link.client.js                                                                                friendly-errors 16:19:36
* core-js/modules/es7.array.includes in ./.nuxt/store.js, ./.nuxt/components/nuxt-link.client.js and 1 other                                                            friendly-errors 16:19:36
* core-js/modules/es7.object.get-own-property-descriptors in ./.nuxt/utils.js                                                                                           friendly-errors 16:19:36
* core-js/modules/es7.promise.finally in ./.nuxt/client.js                                                                                                              friendly-errors 16:19:36
* core-js/modules/es7.symbol.async-iterator in ./.nuxt/client.js, ./.nuxt/components/nuxt-link.client.js                                                                friendly-errors 16:19:36
* core-js/modules/web.dom.iterable in ./.nuxt/App.js, ./.nuxt/components/nuxt-link.client.js

確かに core-js@2.6.7 を追加することでエラーは解消し、正常に動作するようになりますが、どうやら yarn.lock を削除して node_modules を構築し直すとエラーは解消するようでした。
バージョンアップによって依存関係が正しく変更されなかった影響のようです。

参考サイト

Nuxt 2.5.0 にバージョンアップしたら core-js のエラーが出るようになった
core-js problems with 2.5.0


Written with StackEdit.

2019年9月25日水曜日

`vuex-class-component` のバージョンアップにはまった

NuxtでFirebaseのAuthenticationを使った認証を行う」でつかったプロジェクトのモジュールをアップグレードした際に vuex-class-component がバージョンアップしていたことではまったので、その内容と解決方法を記録しておきます。

修正内容は以下のリポジトリに反映してあります。
https://github.com/TAC/nuxt-firebase-auth-example

Nuxtを2.9.2にバージョンアップ

Nuxt を 2.4.0 から 2.9.2 にバージョンアップする」でやった手順をもとにバージョンアップしたところ、以下のエラーが発生するようになりました。

ERROR  [Vue warn]: Error in render: "TypeError: Cannot redefine property: user"

当初はどこに問題があるのかわからなかったのですが、調べていくうちに vuex-class-component1.6.0 から 2.0.4 へ大きくバージョンアップして、使用方法が変わっていることにたどりつきました。
さらに調べた結果、バージョンアップの影響で stategetter を付与する方法だと、上記のエラーが出るようになってしまっていたので、該当箇所は以下のように変更しました。

app/src/store/models/users.ts

- @getter user: User | null = null
+ private user: User | null = null

+ get get() {
+   return this.user
+ }

app/src/pages/index.vue

export default class extends Vue {
- @Users.Getter('user') user
+ @Users.Getter('get') user

[追記:2019-10-26]

上記の対応方法を間違えていたようです。
初期値にnullundefineを設定していた場合に出るエラーのようでした。
以下で訂正します。

- @getter user: User | null = null
+ @getter user: User = {}

  get isAuthenticated() {
-   return !!this.user
+   return !!this.user.uid
  }

  @mutation
  public UNSET_USER() {
-   this.user = null
+   this.user = {}
  }

さらなるエラー

これでビルドは通ったのですが、Sign-In Google のボタンを押下したときに以下のエラーが出るようになりました。

[vuex] unknown local action type: modules/signIn, global type: modules/auth/modules/signIn

どうやらモジュールモードでのパスの設定がうまく行っていないようでした。
該当箇所を以下のように修正することで対応できました。

app/src/store/modules/auth.ts

- @action()
+ @action({ mode: 'raw' })
  public async signInGoogle() {
+   const context = getRawActionContext(this)
    const provider = new firebase.auth.GoogleAuthProvider()
-   await this.signIn(provider)
+   await context.dispatch('signIn', provider)
  }

Written with StackEdit.

2019年9月24日火曜日

Nuxt.js v2.8.0 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/nuxt/nuxt.js/releases/tag/v2.8.0

😎 Developer Experience

開発者の体験

  • #5770 Add a group for SSR logs to avoid polluting the browser console
  • #5810 Fancier browser logs with consola
  • #5820, #5832, #5834 Show build indicator in the browser when rebuilding code:
  • #5753 Watch for pages/ creation when default page displayed
  • #5812 Only listen for file changes for supported extensions
  • #5753 Re-use the same port when randomly assigned when restarting in dev mode

  • #5770 ブラウザコンソールを汚染しないように、SSRログのグループを追加します。
  • #5810 consolaを使用した派手なブラウザーログ
  • #5820, #5832, #5834 コードを再構築するときに、ブラウザにビルドインジケーターを表示します。
  • #5753 デフォルトのページが表示されたら、 pages/ の作成に注意してください。
  • #5812 サポートされている拡張子のファイル変更のみをリッスンします。
  • #5753 開発モードで再起動するときにランダムに割り当てられた場合、同じポートを再使用します。

🐛 Bug Fixes

バグ修正

  • vue-renderer
    • #5807 Add User-Agent‍ to Vary header in modern server mode
    • #5764 Safe format SSR logs
  • server
    • #5793 Preserve random port when restarting
    • Return listener when calling listen
  • builder
    • #5753 Watch for pages/ creation when default page displayed
    • #2812 Only listen for file changes for supported extensions
  • generator
    • #5791 Minify spa fallback
  • types
    • #5785 Add type definition for functional babel.presets
  • vue-app
    • #5757 Reuse page component with watchQuery
    • #5746 Remove trailing slash in vue-router non-strict mode
    • #5752 Don’t attach catch handler to already loaded component
    • #5824 fixPrepatch in-out transition fix (issue #5797)
  • utils
    • #5754 Handle serializeFunction edge case

  • vue-renderer
    • #5807 最新のサーバーモードで User-Agent‍Vary ヘッダーに追加
    • #5764 安全な形式のSSRログ
  • server
    • #5793 再起動時にランダムなポートを保持
    • listenを呼び出すときにリスナーを返す
  • builder
    • #5753 デフォルトのページが表示されたときに pages/ の作成を監視する
    • #2812 サポートされている拡張子のファイル変更のみをリッスンします
  • generator
    • #5791 SPAのフォールバックを縮小
  • types
    • #5785 機能的な babel.presets のタイプ定義を追加
  • vue-app
    • #5757 watchQueryでページコンポーネントを再利用します
    • #5746 vue-router non-strictモードで末尾のスラッシュを削除
    • #5752 既に読み込まれているコンポーネントにキャッチハンドラーをアタッチしないでください
    • #5824 fixPrepatchインアウト遷移の修正 (issue #5797)
  • utils
    • #5754 serializeFunction エッジケースの処理

🚀 Features

機能

  • vue-renderer
    • #5745 Add render.injectScripts option
    • #5784 Support render.ssrLog for controlling SSR logs

  • #5745 render.injectScripts オプションを追加
  • #5784 SSRログを制御するための render.ssrLog のサポート

💅 Refactors

リファクタ

  • core
    • #5796 Use require.resolve instead of Module internals
  • builder
    • #5792 Pass nuxt options to template as nuxtOptions
  • vue-app
    • #5770 Add a group for SSR logs
    • #5826 simplify mount error log
  • general
    • #5748 Small readability improvements

  • core
    • #5796 Module 内部の代わりに require.resolve を使用
  • builder
    • #5792 nuxtオプションを nuxtOptions としてテンプレートに渡します
  • vue-app
    • #5770 SSRログのグループを追加します
    • #5826 マウントエラーログを簡素化
  • general
    • #5748 小さな読みやすさの改善

📝 Examples

事例

  • auth-jwt
    • #5775 Use named store export to prevent warning
  • typescript
    • #5742 Add missing ts-node dependency

  • auth-jwt
    • #5775 警告を防止するために名前付きストアのエクスポート(※)を使用する
  • typescript
    • #5742 欠落しているts-node依存関係を追加

(※) Vuexモジュールモードのことと思われる


🏡 Chore

雑用

  • ci
    • #5802 Upload test report to CircleCI and Azure
    • Add flags for codecov
    • Enable audit
  • general
    • Remove unused dependency cross-env
    • Add FUNDING.md
    • Improve links in readme

  • ci
    • #5802 テストレポートをCircleCIおよびAzureにアップロードする
    • codecovのフラグを追加
    • 監査を有効にする
  • general
    • 未使用の依存関係 cross-env を削除します
    • FUNDING.md を追加します
    • readme のリンクを改善する

♻️ Tests

テスト

  • general
    • #5790 Add unit tests for core/resolver.js
    • #5782 Remove duplicate unit tests in packages/core/test/resolver

  • general
    • #5790 core/resolver.js の単体テストを追加
    • #5782 packages/core/test/resolver の重複した単体テストを削除します

Written with StackEdit.

2019年9月17日火曜日

firebase-tools v7.2.1 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.2.1

  • Fixed bug causing Realtime Database emulator to serve errors on the .inspect/coverage.json route.
  • Fixed bug causing Realtime Database emulator to reject requests with Host headers not containing "localhost".
  • Fixed regression in deployment with service account.

  • Realtime Databaseエミュレーターが .inspect/coverage.json ルートでエラーを提供する原因となるバグを修正しました。
  • Realtime Databaseエミュレーターが "localhost" を含まない Host ヘッダーのリクエストを拒否するバグを修正しました。
  • サービスアカウントを使用した展開のリグレッションが修正されました。

翻訳メモ

  • Fixed bug causing Realtime Database emulator to serve errors on the .inspect/coverage.json route.

こちらの .inspect/coverage.json` route というのがよくわからなかったので調べました。
Realtime Database エミュレータ起動時に http://localhost:9000/.inspect/coverage?ns=<database_name> にアクセスするとテストレポートが生成されるようです。
その際に起きていたエラーのバグを修正したということのようでした。

Error: Unexpected server error. At /.inspect/coverage.json?ns=foo
テストレポートを生成する


Written with StackEdit.

2019年9月6日金曜日

firebase-tools v7.2.0 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.2.0

  • Allow the RTDB emulator to hot-reload rules file on changes.
  • Allows emulated Cloud Functions to talk to production RTDB/Firestore if the emulators are not running.
  • Fixes an issue where internal logs would sometimes appear in stdout.
  • Improves RTDB emulator support for JWT algorithms and arbitrary Google Cloud auth tokens.
  • Fixes an issue where functions:shell logs could be double escaped.
  • Fixes an issue with false noisy logging about emulators not running.
  • Allow starting the RTDB or Firestore emulators with any project ID.

  • RTDBエミュレータが変更時に rules ファイルをホットリロードできるようにします。
  • エミュレーターが実行されていない場合、エミュレートされたCloud Functionsが実稼働しているRTDB/Firestoreと通信できるようにします。
  • 内部ログが標準出力に表示されることがある問題を修正しました。
  • JWTアルゴリズムと任意のGoogle Cloud認証トークンのRTDBエミュレーターサポートを改善します。
  • functions:shell ログが二重にエスケープされる問題を修正しました。
  • エミュレーターが実行されていないという誤ったノイズのあるログの問題を修正
  • 任意のプロジェクトIDでRTDBまたはFirestoreエミュレーターを起動できます。

Written with StackEdit.

2019年9月4日水曜日

Nuxt を 2.4.0 から 2.9.2 にバージョンアップする

2.9.0 から TypeScript の対応が変わったのでバージョンバップ時の手順を残しておきます。

こちらのリポジトリをバージョンアップしました。
https://github.com/TAC/nuxt-typescript-example/tree/Nuxt_2.4.0

バージョンアップ後はこちらになります。
https://github.com/TAC/nuxt-typescript-example

モジュールのアップグレード

まずは npm-check-updates を使って一括でアップグレードしてしまいます。

> ncu -u
Upgrading C:\work\repos\nuxt\typescript\app\package.json
[====================] 24/24 100%

 nuxt                                ^2.4.0  →     ^2.9.2
 nuxt-property-decorator             ^2.1.3  →     ^2.4.0
 ts-node                             ^8.1.0  →     ^8.3.0
 @nuxt/typescript                    ^2.7.1  →     ^2.8.1
 @nuxtjs/eslint-config               ^0.0.1  →     ^1.1.2
 @typescript-eslint/eslint-plugin    ^1.9.0  →     ^2.0.0
 babel-eslint                       ^10.0.1  →    ^10.0.3
 eslint                             ^5.15.1  →     ^6.3.0
 eslint-config-prettier              ^4.1.0  →     ^6.1.0
 eslint-config-standard            >=12.0.0  →   >=14.1.0
 eslint-loader                       ^2.1.2  →     ^3.0.0
 eslint-plugin-import              >=2.16.0  →   >=2.18.2
 eslint-plugin-jest                >=22.3.0  →  >=22.16.0
 eslint-plugin-node                 >=8.0.1  →    >=9.2.0
 eslint-plugin-nuxt                 >=0.4.2  →    >=0.4.3
 eslint-plugin-prettier              ^3.0.1  →     ^3.1.0
 eslint-plugin-promise              >=4.0.1  →    >=4.2.1
 eslint-plugin-standard             >=4.0.0  →    >=4.0.1
 eslint-plugin-vue                   ^5.2.2  →     ^5.2.3
 nodemon                            ^1.18.9  →    ^1.19.1
 prettier                           ^1.16.4  →    ^1.18.2
 rimraf                              ^2.6.3  →     ^3.0.0

Run npm install to install new versions.

package.json が更新されるので yarn でモジュールを更新します。

そして、不要になったモジュールをアンインストールします。

yarn remove ts-node @nuxt/typescript @nuxtjs/eslint-config @typescript-eslint/eslint-plugin

次に新しく必要となったモジュールを追加します。

yarn add @nuxt/typescript-runtime
yarn add -D @nuxt/typescript-build @nuxtjs/eslint-config-typescript

tsconfig.json を修正します。

app/tsconfig.json

{
  "compilerOptions": {
    "types": [
      "@types/node",
-     "@nuxt/vue-app"
+     "@nuxt/types"
    ]

nuxt.config.ts を修正します。

app/nuxt.config.ts

-import NuxtConfiguration from '@nuxt/config'
+import { Configuration } from '@nuxt/types'

-const nuxtConfig: NuxtConfiguration = {
+const nuxtConfig: Configuration = {

+  buildModules: ['@nuxt/typescript-build'],

package.json を修正して、nuxt コマンドを nuxt-ts コマンドに変更します。

app/package.json

  "scripts": {
-   "dev": "nuxt",
-   "build": "nuxt build && yarn build:copy",
+   "dev": "nuxt-ts",
+   "build": "nuxt-ts build && yarn build:copy",

.eslintrc.js を修正して ESLint の設定も変更します。

app/.eslintrc.js

  extends: [
-   '@nuxtjs',
+   '@nuxtjs/eslint-config-typescript',

これだけだと以下の警告メッセージが出ます。

=============

WARNING: You are currently running a version of TypeScript which is not officially supported by typescript-estree.

You may find that it works just fine, or you may not.

SUPPORTED TYPESCRIPT VERSIONS: >=3.2.1 <3.6.0

YOUR TYPESCRIPT VERSION: 3.6.2

Please only submit bug reports when using the officially supported version.

=============

これは TypeScript のバージョンが新しすぎた場合の警告で、.eslintrc.jsparserOptions で表示を抑制できるみたいですが、根本的な解決は依存しているモジュールのバージョンが新しくなることのようなので対応を待つしかないようです。

app/.eslintrc.js

module.exports = {
  parserOptions: {
+   'warnOnUnsupportedTypeScriptVersion': false
  },

functions のモジュールもアップグレード

app と同様の手順でアップグレードします。

> ncu -u                                                                                                                                      Upgrading C:\work\repos\nuxt\typescript\functions\package.json
[====================] 14/14 100%

 @nuxt/config              ^2.7.1  →   ^2.9.2
 cross-env                 ^5.2.0  →   ^5.2.1
 express                  ^4.17.0  →  ^4.17.1
 firebase                  ^6.0.2  →   ^6.5.0
 firebase-admin            ~7.0.0  →   ~8.4.0
 firebase-functions        ^2.3.0  →   ^3.2.0
 nuxt                      ^2.4.0  →   ^2.9.2
 nuxt-property-decorator   ^2.1.3  →   ^2.4.0
 ts-node                   ^8.1.0  →   ^8.3.0
 @nuxt/typescript          ^2.7.1  →   ^2.8.1
 @types/express           ^4.16.1  →  ^4.17.1
 tslint                   ^5.12.0  →  ^5.19.0
 typescript                ^3.2.2  →   ^3.6.2

Run npm install to install new versions.

次に Configuration インターフェースの取得先が @nuxt/config から @nuxt/types に変わったので、モジュールを入れ替えます。

> yarn remove @nuxt/config
> yarn add -D @nuxt/types

Nuxtの設定している箇所を修正します。

functions/src/index.ts

-import NuxtConfiguration from '@nuxt/config'
+import { Configuration } from '@nuxt/types'

-const nuxtConfig: NuxtConfiguration = {
+const nuxtConfig: Configuration = {

あとは以前と同じ手順でビルドして、firebase serve で動作確認できます。


以上の設定で 2.4.0 から 2.9.2 にバージョンアップすることができました!

参考サイト

Migration from Nuxt 2.8
Nuxt.js 2.9でのTypeScript対応
typescript環境でALEのeslint利用fixerが動かなくなっていたことの対処方法


Written with StackEdit.

2019年8月28日水曜日

NuxtでFirebaseのAuthenticationを使った認証を行う

FirebaseAuthenticationを使用しての認証機能の実装方法の紹介になります。

完成したプロジェクトは以下のリポジトリになります。
https://github.com/TAC/nuxt-firebase-auth-example

Firebaseの設定

最初にFirebaseの設定を行います。
Firebaseのアカウント作成や設定などは他のサイトなどで確認して下さい。
接続情報は.env/development.jsに記載してnuxt.config.tsで読み込むようにします。
これはリポジトリに入れていないので、上記リポジトリを clone した場合は自分で作成してください。

.env/development.js

module.exports = {
  apiKey: "<your apiKey>",
  authDomain: "<your authDomain>",
  databaseURL: "<your databaseURL>",
  projectId: "<your projectId>",
  storageBucket: "<your storageBucket>",
  messagingSenderId: "<your api messagingSenderId>"
}

app/nuxt.config.ts

import NuxtConfiguration from '@nuxt/config'
import pkg from './package.json'
import envSet from '../.env/environment.js'

const nuxtConfig: NuxtConfiguration = {
  mode: 'universal',
  srcDir: 'src',

  env: envSet,
  ...
}

設定値はprocess.env.apiKeyという形式で利用できます。

次にFirebase の初期設定を行うpluginsを作成します。

app/src/plugins/firebase.js

import firebase from 'firebase/app'

if (!firebase.apps.length) {
  firebase.initializeApp({
    apiKey: process.env.apiKey,
    authDomain: process.env.authDomain,
    databaseURL: process.env.databaseURL,
    projectId: process.env.projectId,
    storageBucket: process.env.storageBucket,
    messagingSenderId: process.env.messagingSenderId
  })
}

export default firebase

FirebaseSDKを使用する箇所でこのプラグインを読み込んで使うことになります。

認証モジュール

Vuex.Store に認証モジュールを作成します。

app/src/store/modules/auth.ts

import {
  VuexModule,
  Module,
  action,
  getRawActionContext
} from 'vuex-class-component'
import firebase from '@/plugins/firebase'
import 'firebase/auth'

@Module({ namespacedPath: 'modules/auth/', target: 'nuxt' })
class Store extends VuexModule {
  @action({ mode: 'raw' })
  public async signIn(provider) {
    const context = getRawActionContext(this)
    context.commit('models/users/UNSET_USER', null, { root: true })
    await firebase
      .auth()
      .signInWithPopup(provider)
      .then(result => {
        if (result.user) {
          context.commit('models/users/SET_USER', result.user, { root: true })
        }
      })
      .catch(error => {
        console.error(error)
        throw error
      })
  }

  @action()
  public async signInGoogle() {
    const provider = new firebase.auth.GoogleAuthProvider()
    await this.signIn(provider)
  }

  @action({ mode: 'raw' })
  public async signOut() {
    const context = getRawActionContext(this)
    await firebase
      .auth()
      .signOut()
      .then(() => {
        context.commit('models/users/UNSET_USER', null, { root: true })
      })
  }

  @action({ mode: 'raw' })
  public isSignIn() {
    const context = getRawActionContext(this)
    return new Promise(resolve => {
      const unsubscribe = firebase.auth().onAuthStateChanged(user => {
        unsubscribe()
        if (user) {
          context.commit('models/users/SET_USER', user, { root: true })
        }
        resolve(user || false)
      })
    })
  }
}

export default Store.ExtractVuexModule(Store)

渡されたプロバイダーの認証を行う signIn アクションと認証データを削除する signOut アクション、サインイン状態の確認を行う isSignIn アクションがあります。
プロバイダーに firebase.auth.GoogleAuthProvider() を渡すとGoogleアカウントの認証ができます。
これを signInGoogle アクションとして作成しておきます。
他にもTwitterやFacebookが認証プロバイダーとして用意されています。
https://firebase.google.com/docs/auth/web/start#next_steps

認証データの保存

firebase.auth().onAuthStateChanged() で取得できるユーザデータを Vuex.Store に保存しておきます。
最初、ユーザデータをそのまま Vuex.Store に保存しようとしたのですが、エラーで動かなかったので修正しました。

参考記事 : Firebase AuthenticationとVuexの合わせ技バグでハマった

app/src/store/models/users.ts

import { VuexModule, Module, getter, mutation } from 'vuex-class-component'

interface User {
  [key: string]: any
}

@Module({ namespacedPath: 'models/users/', target: 'nuxt' })
class Store extends VuexModule {
  @getter user: User | null = null

  get isAuthenticated() {
    return !!this.user
  } 

  @mutation
  public SET_USER(payload) {
    this.user = {
      uid: payload.uid,
      displayName: payload.displayName,
      email: payload.email,
      emailVerified: payload.emailVerified,
      isAnonymous: payload.isAnonymous,
      phoneNumber: payload.phoneNumber,
      photoURL: payload.photoURL,
      providerData: payload.providerData
    }
  }

  @mutation
  public UNSET_USER() {
    this.user = null
  }
}

export default Store.ExtractVuexModule(Store)

認証のテスト

FirebaseSDKをモック化してくれるモジュールをインストールします。(※ただし、このモジュールは残念なことに現在メンテされていません。)

yarn add -D firebase-mock

セットアップ手順をもとに以下のファイルを作成します。

app/tests/__mocks__/plugins/firebase.js

import firebasemock from 'firebase-mock'

const mockauth = new firebasemock.MockAuthentication()
const mockdatabase = new firebasemock.MockFirebase()
const mockfirestore = new firebasemock.MockFirestore()
const mockstorage = new firebasemock.MockStorage()
const mockmessaging = new firebasemock.MockMessaging()

const mocksdk = new firebasemock.MockFirebaseSdk(
  // use null if your code does not use RTDB
  (path) => {
    return path ? mockdatabase.child(path) : mockdatabase
  },
  // use null if your code does not use AUTHENTICATION
  () => {
    return mockauth
  },
  // use null if your code does not use FIRESTORE
  () => {
    return mockfirestore
  },
  // use null if your code does not use STORAGE
  () => {
    return mockstorage
  },
  // use null if your code does not use MESSAGING
  () => {
    return mockmessaging
  }
)

export default mocksdk

@/plugins/firebase の読み込みがモックになるように jest.config.js で設定します。

app/jest.config.js

  moduleNameMapper: {
    '^vue$': 'vue/dist/vue.common.js',
+   '^@/plugins/firebase$': '<rootDir>/tests/__mocks__/plugins/firebase.js',
    '^@/(.*)$': '<rootDir>/src/$1',
    '^~/(.*)$': '<rootDir>/src/$1'
  },

これで Jest での実行時には @/plugins/firebase の読み込みがすべてモックになります。

次に store/modules/auth.ts のテストを書いていきます。

app/tests/store/modules/auth.test.js

import 'firebase/auth'
import Vuex from 'vuex'
import { cloneDeep } from 'lodash'
import { createLocalVue } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import firebase from '@/plugins/firebase'
import auth from '@/store/modules/auth.ts'
import users from '@/store/models/users.ts'

const localVue = createLocalVue()
localVue.use(Vuex)

describe('store/modules/auth.ts', () => {
  let store
  let user

  beforeAll(() => {
    firebase.auth().autoFlush()
  })

  beforeEach(() => {
    // create store
    store = new Vuex.Store({
      modules: {
        'modules/auth': cloneDeep(auth),
        'models/users': cloneDeep(users)
      }
    })

    // mock data
    user = {
      uid: 'alice',
      email: 'alice@mail.com'
    }

    // initialize auth
    firebase
      .auth()
      .createUser(user)
      .then(user => {
        firebase.auth().changeAuthState(user)
      })
  })

  describe('actions', () => {
    test('isSignIn', async () => {
      await store.dispatch('modules/auth/isSignIn')
      const result = store.getters['models/users/user']
      expect(result).toMatchObject(user)
    })

    test('signOut', async () => {
      await store.dispatch('modules/auth/signOut')
      const result = store.getters['models/users/user']
      expect(result).toBeNull()
    })

    test('signInGoogle', async () => {
      firebase.auth().signInWithPopup = jest.fn(provider => {
        return new Promise(resolve => {
          user.providerData = [provider]
          resolve({
            user
          })
        })
      })

      await store.dispatch('modules/auth/signInGoogle')
      await flushPromises()

      const result = store.getters['models/users/user']
      expect(result.providerData[0]).toMatchObject({
        providerId: 'google.com'
      })
    })
  })
})

ポイントとしては以下の2点になります。

  • beforeEach() で初期化時に firebase.auth().autoFlush() を呼び出し、 firebase.auth().createUser() でモック用の認証データを作成しておく
  • firebase.auth().signInWithPopup をモック化する

firebase.auth().autoFlush() を呼び出すことで firebase-mock でモック化された firebase.auth() での変更が反映されるようになります。
また、firebase.auth().signInWithPopup はすでにモックになってますが、期待通りの動きはしてくれません。
なので、正しく動作する形に再度モック関数を設定し直しています。

認証ボタンの作成

最後に作成したアクションを呼び出すボタンを pages/index.vue に配置します。

app/src/pages/index.vue

<template lang="pug">
  section.container
    template(v-if='isAuthenticated')
      ButtonsAction(@action='signOut')
        template(v-slot:buttonText) SignOut
    template(v-else)
      ButtonsAction(@action='signInGoogle')
        template(v-slot:buttonText) Sign-In Google
</template>

<script lang="ts">
import { Component, Vue, namespace } from 'nuxt-property-decorator'
import ButtonsAction from '@/components/buttons/Action.vue'

const Users = namespace('models/users')
const Auth = namespace('modules/auth')

@Component({
  components: {
    ButtonsAction
  }
})
export default class extends Vue {
  @Users.Getter('user') user
  @Users.Getter('isAuthenticated') isAuthenticated
  @Auth.Action('signInGoogle') signInGoogle
  @Auth.Action('signOut') signOut
}
</script>

これで Sign-In Google のボタンが表示され、押すとGoogleアカウント認証のウィンドウがポップアップされると思います。
そのウィンドウで任意のアカウントを選択すると認証が完了します。(GoogleChromeで認証済みの場合はパスワードの入力がスキップされると思います。)
認証が完了するとボタンが SignOut に変わりユーザIDと名前が表示されるかと思います。

SSRに対応する

これでサインイン、サインアウトの実装は終わったのですが、サインインした状態でリロードするとボタンの表示やユーザ情報の表示がちらつくのに気づくと思います。

これはSSR時に認証情報が取得できないためです。
Firebase Authentication では認証情報を IndexedDB に保存しているため、IndexedDB にアクセスできないサーバ側の処理では認証情報を取得できないのです。

そこでサーバ側の処理でも認証情報を取得できるように cookie に認証トークンを保存するように変更します。

Cookieの保存

まずは以下のモジュールを追加します。

yarn add cookie-universal-nuxt jwt-decode core-js@2.6.7

cookie-universal-nuxt はフロント側でもサーバ側でも簡単に cookie にアクセスできるようにしてくれるモジュールです。$cookies をコンテキストに追加してくれます。

jwt-decode はJWTトークンをデコードしてくれるそのまんまのモジュールです。Firebase Authentication で使う認証トークンがJWTトークンなのでこれでデコードします。

core-js は後述するシリアライズ処理で必要になります。
(Nuxt.js 2.8.1 での問題かもしれませんが追加しないとエラーが出ます。)

[追記:2019-09-28]
上記の対応は正しくなかったようです。
詳しくはこちらの記事で説明しています。

次に cookie を操作するヘルパー関数を作成します。

app/src/helpers/cookies.ts

const _OPTIONS = {
  path: '/',
  maxAge: 60 * 60 * 24 * 7,
  secure: true
}

export function getCookie(cookies, name) {
  const data = deserialize(cookies.get('__session'))
  return data[name]
}

export function setCookie(cookies, name, value, isLocalhost) {
  const data = deserialize(cookies.get('__session'))
  data[name] = value
  _OPTIONS.secure = !isLocalhost
  cookies.set('__session', serialize(data), _OPTIONS)
}

export function removeCookie(cookies, name, isLocalhost) {
  const data = deserialize(cookies.get('__session'))
  delete data[name]
  _OPTIONS.secure = !isLocalhost
  cookies.set('__session', serialize(data), _OPTIONS)
}

function serialize(obj) {
  try {
    const str = JSON.stringify(obj, function replacer(k, v) {
      if (typeof v === 'function') {
        return v.toString()
      }
      return v
    })
    return str
  } catch (e) {
    return {}
  }
}

function deserialize(str) {
  try {
    const obj = JSON.parse(str, function reciever(k, v) {
      if (typeof v === 'string' && v.startsWith('function')) {
        return Function.call(this, 'return ' + v)()
      }
      return v
    })
    return obj
  } catch (e) {
    return {}
  }
}

注意点は __session という名前で保存する必要がある点です。Firebaseでホスティングする場合、これ以外の名前の cookie は削除されてしまいます。
https://firebase.google.com/docs/hosting/manage-cache#using_cookies

__session 以外の名前が使えないので、この中に連想配列をシリアライズして登録します。

認証トークンの保存

以下に記載する箇所で認証トークンの保存と読み込みを行います。

app/src/pages/index.vue

  private signInGoogle() {
    const that = this as any
    this.authSignInGoogle().then(() => {
      const currentUser = firebase.auth().currentUser
      if (currentUser) {
        currentUser.getIdToken().then(token => {
          setCookie(that.$cookies, 'token', token, that.$isLocalhost)
        })
      }
    })
  }

  private signOut() {
    const that = this as any
    this.authSignOut().then(() => {
      removeCookie(that.$cookies, 'token', that.$isLocalhost)
    })
  }

サインインした時に認証トークンを取得して保存、サインアウトした時に保存してある認証トークンを削除します。

app/src/plugins/auth.js

import firebase from '@/plugins/firebase'
import 'firebase/auth'
import { setCookie } from '@/helpers/cookies'

export default async function({ store, app }) {
  await store.dispatch('modules/auth/isSignIn')

  const currentUser = firebase.auth().currentUser
  if (currentUser) {
    currentUser.getIdToken().then(token => {
      setCookie(app.$cookies, 'token', token, app.$isLocalhost)
    })
  }
}

認証プラグインを作成して、リロード時に認証トークンが保存されるようにします。

app/src/store/index.ts

import jwtDecode from 'jwt-decode'
import { getCookie } from '@/helpers/cookies'

export const actions = {
  nuxtServerInit: ({ commit }, { app }) => {
    const token = getCookie(app.$cookies, 'token')
    if (token) {
      commit('models/users/SET_USER', jwtDecode(token))
    }
  }
}

Vuex.storenuxtServerInit 処理時に保存してある認証トークンからユーザデータを取得します。(今回はユーザデータをFirestoreなどから取得していないのでこのような形ですが、サーバ側からデータ取得する際には認証トークンの検証が必要になります。)

認証トークンから取得できるユーザデータが onAuthStateChanged とは若干違うのでユーザデータの保存処理も修正します。(ちょっと無理やりですが・・・)

app/src/store/models/users.ts

  @mutation
  public SET_USER(payload) {
    const uid = payload.uid !== undefined ? payload.uid : payload.user_id
    const displayName =
      payload.displayName !== undefined ? payload.displayName : payload.name
    const emailVerified =
      payload.emailVerified !== undefined
        ? payload.emailVerified
        : payload.email_verified
    const photoURL =
      payload.photoURL !== undefined ? payload.photoURL : payload.picture
    this.user = {
      uid,
      displayName,
      email: payload.email,
      emailVerified,
      isAnonymous: payload.isAnonymous,
      phoneNumber: payload.phoneNumber,
      photoURL,
      providerData: payload.providerData
    }
  }

これでサインインしたあとにリロードしてもボタン等がちらつかなくなったと思います。

まとめ

ざっくりとですが Firebase Authentication を使った認証の実装の一例を紹介しました。

Firebase は他にも便利な機能があるのでもっと紹介してきたいと思います!


Written with StackEdit.

2019年8月26日月曜日

firebase-tools v7.1.1 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.1.1

  • Fixes issue deploying scheduled functions for non us-central project locations.
  • Cleans up unnecessary emulator JAR files when a new one is downloaded.

  • us-central 以外のプロジェクトの場所にスケジュールされた機能をデプロイする際の問題を修正しました。
  • 新しいファイルがダウンロードされたときに、不要なエミュレータJARファイルをクリーンアップします。

Written with StackEdit.

2019年8月23日金曜日

firebase-tools v7.1.0 release note 和訳

※翻訳勉強中のため、間違っている可能性があります。


https://github.com/firebase/firebase-tools/releases/tag/v7.1.0

  • Auto-download Firestore and RTDB emulators when using emulators:start.
  • Make functions:shell respect the --port argument.
  • Improve error message when firebase serve can’t acquire the right port.
  • Allow running the Firestore and RTDB emulators without a configuration.
  • Fixes bug where profiler table rendering crashes due to undefined path.
  • Allow the Firestore emulator to give more information about invalid rulesets.
  • Hot reload firestore rules on change.
  • Upgrade archiver dependency to 3.0.0.
  • Update functions init templates to v3.1.0
  • Fix emulation of https functions.
  • Fix bug where calling clearFirestoreData against the Firestore Emulator fails if more than 500 documents exist.
  • Fix multipart uploads in emulator.
  • Fixes long responses from emulator being truncated.
  • Fixes a bug where admin.firestore() and app.firestore() behaved differently.
  • Fix issue with npm 6.10 where an internal version check of the firebase-functions SDK caused a crash.
  • Fix bug in Firestore emulator where queries with startAfter had incorrect boundary behavior.
  • Fix bug where too many functions would hang the emulator.
  • Improves ability of Firebase CLI to be used with service accounts.

  • emulators:start を使うときは、FirestoreとRTDBエミュレータを自動ダウンロードします。
  • functions:shell--port 引数を尊重するようにします。
  • firebase serve が正しいポートを取得できない場合のエラーメッセージを改善しました。
  • 設定なしでFirestoreおよびRTDBエミュレータを実行することを許可します。
  • 未定義の path が原因でプロファイラテーブルのレンダリングがクラッシュするバグを修正しました。
  • Firestoreエミュレータが無効なルールセットについてより多くの情報を提供できるようにします。
  • Firestore rules の変更時にホットリロードします。
  • アーカイバの依存関係を3.0.0にアップグレードします。
  • Functions 初期化テンプレートを v3.1.0 に更新。
  • https Functionsのエミュレーションを修正しました。
  • Firestore Emulatorに対して500を超えるドキュメントが存在すると、clearFirestoreData の呼び出しが失敗するというバグを修正しました。
  • エミュレータでのマルチパートアップロードを修正しました。
  • エミュレータからの長い応答が切り捨てられるのを修正。
  • admin.firestore()app.firestore() の動​​作が異なるというバグを修正しました。
  • firebase-functions SDKの内部バージョンチェックがクラッシュを引き起こすnpm 6.10 の問題を修正しました。
  • startAfterを使ったクエリが誤った境界動作をしていたFirestoreエミュレータのバグを修正しました。
  • Functionsが多すぎるとエミュレータをハングさせるというバグを修正しました。
  • Firebase CLIがサービスアカウントで使用される機能が向上しました。

Written with StackEdit.