ラベル firebase の投稿を表示しています。 すべての投稿を表示
ラベル firebase の投稿を表示しています。 すべての投稿を表示

2020年2月20日木曜日

firebase-tools v7.9.0 release note 和訳

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


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

  • Adds support for immutable params in ext:configure.
  • Fixes an issue where console.log() sometimes printed incorrectly (#1817)
  • Improved Firebase App Distribution binary uploading.

  • ext:configure で不変パラメーターのサポートを追加します。
  • console.log() が時々間違って表示される問題を修正しました。(#1817)
  • Firebase App Distributionバイナリアップロードを改善しました。

Written with StackEdit.

2020年1月22日水曜日

firebase-tools v7.8.1 release note 和訳

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


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

  • Improved error handling when exporting users.
  • Fixes Firebase ext command and enables it to run without a project.
  • Fixes an issue where repeated invoations cause an EADDRINUSE error (#1815).

  • ユーザーのエクスポート時のエラー処理を改善しました。
  • Firebase の ext コマンドを修正し、プロジェクトなしで実行できるようにしました。
  • 繰り返し呼び出しが EADDRINUSE エラーを引き起こす問題を修正しました。(#1815)

Written with StackEdit.

2020年1月4日土曜日

firebase-tools v7.8.0 release note 和訳

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


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

  • Fixes a bug where the Functions emulator ignored the “host” configuration (#1722)
  • Fixes a bug where the Functions emulator accepted requests to too many paths (#1773)
  • Modifies firebase ext:update to not perform update if the extension is already up to date.
  • Print Firebase Console links for Extensions after operations.
  • Updated Firebase Extensions registry address.
  • Adds the firebase init emulators command.
  • Adds a Cloud Pub/Sub Emulator (#1748).
  • Fixes a bug where the Firestore emulator was unable to serve rule coverage HTML reports.
  • Fixes a bug in the Firestore emulator where rapidly overwriting the same document could trigger exceptions.

  • Functions エミュレーターが "host" の構成を無視するバグを修正しました。(#1722)
  • Functions エミュレーターがあまりにも多くのパスへのリクエストを受け入れていたバグを修正しました。(#1773)
  • 拡張機能が既に最新の場合、更新を実行しないようにfirebase ext:updateを変更しました。
  • 操作後に Extensions の Firebase Console リンクを印刷します。
  • Firebase Extensions レジストリアドレスを更新しました。
  • firebase init emulatorsコマンドを追加しました。
  • Cloud Pub / Subエミュレーターを追加しました。(#1748)
  • Firestore エミュレーターがルールカバレッジHTMLレポートを提供できなかったバグを修正します。
  • 同じドキュメントをすばやく上書きすると例外が発生する可能性があった Firestore エミュレータのバグを修正しました。

Written with StackEdit.

2020年1月1日水曜日

firebase-tools v7.7.0 release note 和訳

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


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

  • Fixed an issue in Firestore Emulator where batchGet and transactions does not work over REST.
  • Make additional setIamPolicy call when creating HTTP functions to ensure they continue to be publicly available.
  • Prevent duplicate rulesets from being created.

  • batchGet とトランザクションがRESTで機能しないFirestore Emulatorの問題を修正しました。
  • HTTP Functionsを作成するときに追加の setIamPolicy 呼び出しを行って、それらが引き続き公開されていることを確認します。
  • 重複するルールセットが作成されないようにします。

Written with StackEdit.

2019年12月24日火曜日

firebase-tools v7.6.2 release note 和訳

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


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

  • Fixes a bug where rounds=0 was accepted for SHA1 hashes (#1617).
  • Allows support for using \n in the --releaseNotes option of the appdistribution:distribute command (#1739).
  • Specifies schema version when dealing with IAM Policies related to Extensions.
  • Functions emulator now re-uses workers to avoid running global code on each execution (#1353).
  • Improves error handling of unreachable regions for Cloud Functions deploys.

  • SHA1ハッシュで rounds=0 が受け入れられていたバグを修正しました。(#1617)
  • appdistribution:distribute コマンドの --releaseNotes オプションで \n の使用をサポートします。(#1739)
  • 拡張機能に関連するIAMポリシーを扱うときにスキーマバージョンを指定します。
  • Functionsエミュレーターは、実行ごとにグローバルコードを実行しないようにワーカーを再利用します。(#1353)
  • Cloud Functionsデプロイの到達不能領域のエラー処理を改善します。

Written with StackEdit.

2019年12月20日金曜日

firebase-tools v7.6.1 release note 和訳

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


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

  • Fixes a bug where the wrong updateMask was sent during ext:update.
  • Fixes a bug where users were prompted for unchanged params during ext:update.

  • ext:update 中に間違った updateMask が送信されていたバグを修正しました。
  • ユーザーが ext:update を実行中に変更されていないパラメーターの入力を求められていたバグを修正しました。

Written with StackEdit.

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年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.