リファクタリング: 作成日時と更新日付の追加 (STEP 3 : 他のユーザのタスクが見れるタスク管理アプリを作成する - React + Redux + Firebase チュートリアル)

★前回の記事

yucatio.hatenablog.com

Firebase Realtime Databaseでは、データの作成日時・更新日時は自動で付与されないので、クライアント側(javascript)で付与します。これに伴い、Cloud FunctionsもDatabaseの更新日時を使用するように変更します。データの作成日時・更新日時は、サンプルのアプリでは特に必要がないですが、アプリをリリースした後、問い合わせがあった場合の調査などに重要になります。

actionの更新

タスク作成時に、作成時間_createdAt_updatedAtを付与し、更新時に_updatedAtを更新します。 時刻関連の処理には、使いやすさのため、momentモジュールを使用します。

src/actions/todoActions.js

import moment from 'moment'  // 追加
// 中略

export const addTodo = (uid, text) => {
  return (dispatch, getState, {getFirebase}) => {
    // 中略
    const createdAt = moment().valueOf();  // 追加
    firebase.push(`todos/${uid}`,{
      completed: false,
      text,
      _createdAt : createdAt,  // 追加
      _updatedAt : createdAt  // 追加
    })
    // 中略
  }
}

export const toggleTodo = (uid, id) => {
  return (dispatch, getState, {getFirebase}) => {
    // 中略
    const updatedAt = moment().valueOf();  // 追加
    firebase.update(`todos/${uid}/${id}`, {
      completed: ! todo.completed,
      _updatedAt : updatedAt  // 追加
    })
    // 中略
  }
}

database.rules.jsonの更新

_createdAt_updatedAtを登録できるようにデータベースルールファイルを変更します。

database.rules.json

{
  "rules": {
    "todos": {
      "$uid": {
        ".read": true,
        ".write": "$uid === auth.uid",
        "$todoId": {
          ".validate": "newData.hasChildren(['text', 'completed', '_createdAt', '_updatedAt'])",  // 変更
          "text": {
            ".validate": "newData.isString()"
          },
          "completed": {
            ".validate": "newData.isBoolean()"
           },
          "_createdAt": {  // 追加
            ".validate": "newData.isNumber()"
          },
          "_updatedAt": {  // 追加
            ".validate": "newData.isNumber()"
          },
          "$other": { ".validate": false }
        }
      }
    },
    // 中略
  }
}

変更を反映します。

firebase deploy --only database

".validate": "newData.hasChildren(['text', 'completed', '_createdAt', '_updatedAt’])”があるせいで、既存のタスクが更新できなくなってしまいました。今回は既存のタスクは削除します。次回からは初めから_createdAt_updatedAtは付与しましょう。

Cloud Functionsの修正

タスクの作成トリガを/todos/{uid}/{todoId}/_createdAtの作成、更新トリガを/todos/{uid}/{todoId}/_updatedAtの更新とするように変更します。 また、recentUpdatedTodos_updatedAtは、todos_updatedAtから取得するように変更します。

fuctions/index.js

const addRecentUpdate = (uid, todoId, todo, eventType) => {  // #1
  return admin.database().ref('/users/' + uid + '/displayName').once('value').then((snapshot) => {
    const displayName = snapshot.val();
    return (admin.database().ref('/recentUpdatedTodos/' + todoId).set({
      uid,
      displayName,
      text: todo.text,  // #2
      eventType,
      _updatedAt: todo._updatedAt  // #3
    }));
  });
}

exports.addRecentUpdateOnCreate = functions.database.ref('/todos/{uid}/{todoId}/_createdAt')  // #4
  .onCreate((snapshot, context) => {
    const uid = context.params.uid;
    const todoId = context.params.todoId;

    return snapshot.ref.parent.once('value').then((snapshot) => {  // #5
      const todo = snapshot.val();
      return addRecentUpdate(uid, todoId, todo, 'CREATE');
    });
})

exports.addRecentUpdateOnUpdate = functions.database.ref('/todos/{uid}/{todoId}/_updatedAt')  // #6
  .onUpdate((change, context) => {
    const todoId = context.params.todoId;
    const uid = context.params.uid;

    return change.after.ref.parent.once('value').then((snapshot) => {  // #7
      const todo = snapshot.val();
      return addRecentUpdate(uid, todoId, todo, 'UPDATE');
    });
})
  • addRecentUpdateの引数のうち、texttodoに変更します(#1)。
  • texttodo.textで取得します(#2)。
  • recentUpdatedTodos_updatedAtを、todo_updatedAtから取得します(#3)。
  • データパスを、/todos/{uid}/{todoId}/textから/todos/{uid}/{todoId}/_createdAtに変更します(#4)。
  • 親ノードを取得する処理を追加します(#5)。
  • 関数名を、addRecentUpdateOnUpdateCompletedからaddRecentUpdateOnUpdateに変更し、データパスを、/todos/{uid}/{todoId}/completedから/todos/{uid}/{todoId}/_updatedAtに変更します(#6)。
  • textノードを取得する処理から、親ノードを取得する処理に変更します(#7)。

デプロイします。

$ firebase deploy --only functions

=== Deploying to 'todo-sample-xxxxx’…

i  deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint

> functions@ lint /Users/yuka/react/todo-sample/functions
> eslint .

✔  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (56.34 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: creating Node.js 6 function addRecentUpdateOnUpdate(us-central1)...
i  functions: updating Node.js 6 function addRecentUpdateOnCreate(us-central1)...
i  functions: updating Node.js 6 function limitRecentUpdatedTodos(us-central1)...

今回、functionの名前を変更したので、途中でaddRecentUpdateOnUpdateCompletedを削除するか聞かれます。”y”を選択します。

The following functions are found in your project but do not exist in your local source code:
    addRecentUpdateOnUpdateCompleted(us-central1)

If you are renaming a function or changing its region, it is recommended that you create the new function first before deleting the old one to prevent event loss. For more info, visit https://firebase.google.com/docs/functions/manage-functions#modify
? Would you like to proceed with deletion? Selecting no will continue the rest o
f the deployments. Yes
i  functions: deleting function addRecentUpdateOnUpdateCompleted(us-central1)...
✔  functions[addRecentUpdateOnUpdateCompleted(us-central1)]: Successful delete operation. 
✔  functions[addRecentUpdateOnCreate(us-central1)]: Successful update operation. 
✔  functions[limitRecentUpdatedTodos(us-central1)]: Successful update operation. 
✔  functions[addRecentUpdateOnUpdate(us-central1)]: Successful create operation. 

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/todo-sample-xxxxx/overview

functionのデプロイが完了しました。

動作確認

動作確認をします。

タスクを新規登録します。

f:id:yucatio:20181101144012p:plain:w200

データベースを確認します。

f:id:yucatio:20181101145256p:plain

_createdAt_updatedAtが追加されています。

recentUpdatedTodos_updatedAttodosのものと同じ時刻です。

タスクを更新します。

f:id:yucatio:20181101144642p:plain:w200

データベースを確認します。

f:id:yucatio:20181101144900p:plain

_updatedAtが更新されました。recentUpdatedTodos_updatedAttodosの時刻で更新されています。

以上で作成日時と更新日付の追加の処理の完了です。Firebase Realtime Databaseを使用する際は、最初から全てのデータに作成日時と更新日時を付与することを強くおすすめします。

★次回の記事

yucatio.hatenablog.com

★目次

yucatio.hatenablog.com