CloudStorageとのCORSを解消する
手順
- Cloud SDK を install
- 既存の設定を取得する
- cors対策用のファイルを作成
- デプロイ
- 設定が書き換わっていることを確認
詳細
1. Cloud SDK を install
Cloud SDK Command Line Tools | Cloud SDK: Command Line Interface
2. 既存の設定を取得する
gsutil cors get gs://BUCKET_NAME
3. cors対策用のファイルを作成
touch cors.json
そして、2で取得した既存の値を入れる。 それに加えて、新しく追加したいドメイン名を加える。
4. デプロイ
gsutil cors set cors.json gs://BUCKET_NAME
5. 設定が書き換わっていることを確認
gsutil cors get gs://BUCKET_NAME
CloudStorageとのCORSを解消する
手順
- Cloud SDK を install
- 既存の設定を取得する
- cors対策用のファイルを作成
- デプロイ
- 設定が書き換わっていることを確認
詳細
1. Cloud SDK を install
Cloud SDK Command Line Tools | Cloud SDK: Command Line Interface
2. 既存の設定を取得する
gsutil cors get gs://BUCKET_NAME
3. cors対策用のファイルを作成
touch cors.json
そして、2で取得した既存の値を入れる。 それに加えて、新しく追加したいドメイン名を加える。
4. デプロイ
gsutil cors set cors.json gs://BUCKET_NAME
5. 設定が書き換わっていることを確認
gsutil cors get gs://BUCKET_NAME
Sequelizeで多対多のテーブルジョインを定義する
一つの記事には複数の著者が存在し(共同著者)、著者には複数の記事が結びつく場合、中間テーブルを利用して多対多の状態をつくれるようにしなければならない。
そのように行う方法を説明する。
これには、 belongsToMany
を利用することで多対多のデータ構造を作成することができる。
簡易的な書き方を紹介して、その次に、 as
や foreignKey
を使って細かいことをできる指定の仕方を紹介する。
定義は以下。
Article.belongsToMany(author.factory(sequelize), { through: "ArticleAuthor" });
使い方は以下。
const findAllIncludeParams: IncludeOptions[] = []; findAllIncludeParams.push({ model: Author, attributes: [], required: true }); Article.findAndCountAll({ include: findAllIncludeParams })
これにもっと細かい指定をすると、以下のようになる。
もし、 Article-Author
間の連結が1種類でない場合は、 as
で別名をつけることで、 複数の Article-Author
の連結を行うことができる。
定義は以下。
Article.belongsToMany(author.factory(sequelize), { as: "essay", through: "articleAuthor", foreignKey: "articleId", otherKey: "authorId" }); Article.belongsToMany(author.factory(sequelize), { as: "novel", through: "articleAuthor", foreignKey: "articleId", otherKey: "authorId" });
使い方は以下。
findAllIncludeParams.push({ as: "aaa", model: Author, });
参考
resolveJsonModuleでDate型を使いたい
resolveJsonModule
では json
を読みこみ、 typeof
を与えてあげることで、モックデータから型を自動で生成してくれる。
しかしながら、 json
を読み込む部分では、 Date
型を扱いたくても、 string
と判定されてしまう。
{ ... createdAt: "2000-01-01 00:00:00" ... }
これを Date
型にするには、 json
を読み込むのではなく、以下のような ts
ファイルを読み込ませ、 mockType
を import
することで Date
型を認識させることができる。
export const mockType = { ... createdAt: new Date("2000-01-01 00:00:00") ... }
firebaseのチャットで画像を扱うために
LINEのようなチャットツールでは、チャットしている時系列に画像を表示したり、それとは別に、写真だけを一覧表示することができる。
これがRDBMSでは頭を捻らなくても実装することができるが、firebaseのようなNoSQLでこれを実装するためにはどのようにすれば良いか。
その場合は、 collectionGroup()
を使用してすることで実装することができる。
そのために以下のようなコレクションを定義する。
※C: コレクション ※d: ドキュメント
rooms(C) - room1(d) - name - description - messages(C) - XXX(d) - body - images(C) - members(C) - member1(d) - name - photoUrl
写真を時系列のメッセージと共に流す際には、
db.collection("rooms").doc("room1").collection("messages")
のように普通通りに取得をして、画像だけを一覧したい場合は
db.collection("rooms").doc("room1")collectionGroup("images")
を実行する。
collectionGroupを使うことで、他のmessagesドキュメントの写真を串刺しにして取得することができる。
SequelizeでwhereHasを行う
include内のrequired値をtrueにすることでwhereHasと同じ挙動の動作をさせることができる。 falseの場合は、ORMのリレーションの結果が0件でも取得できる。
attributesを[]
にすることで、whereHasだけを利用することも可能。
XXX.findAll({ where: findAllParams, include: [ { model: YYY, where: { YYYId: 3 }, attributes: ['YYYId'], // カラムの絞り込み required: true } ], limit: limit, offset: offset })
firebaseのAuthenticationとfirestoreを利用したセキュリティルール設定
ここではセキュリティルールを考えた際にfirestoreはどういったデータ構造にするか、ということを説明する。
詳細の設定については、 公式ブログ に譲る。
チャットアプリのような双方向の通信を実現する際は、 firebase
を利用することで簡単に実装できる。
実際には、アプリケーション内で firestore
のデータを監視して、データの追加/変更/削除が発生したら自身で用意したイベントを呼び出すことで、相手の 発信
を受け取ることができる。
ただ、 firestore
は正しいセキュリティルールを設定しないと、 誰でも ストレージにアクセスができるので、必ずルールを設定しなければいけない。
ここで、 firestore
を以下のように組み立てるとする(これは後に困るやり方)。
Collection | Document | Collection/data | Document |
---|---|---|---|
chatRoom | XXX | member(C) | YYY |
message(C) | ZZZ | ||
name(d) | |||
description(d) |
アクセス制御を行うには、以下のような設定を行う。
match /chatRoom/{roomId} { allow read, write: if exists(/databases/$(database)/documents/chatRoom/$(roomId)/member/$(request.auth.uid)); } // 上記以外全て拒否
このようにすることで、 /chatRoom
にアクセスして全データを取得するときに、 /chatRoom/{roomId}
の条件にヒットしないものは取得されない、と思ったが、このパス /chatRoom/{roomId}
はリクエストパスを表現しているため、 このようにアクセス制御を行うと、 /chatRoom
へのルールが設定されていないため、アクセス自体が拒否されてしまう。 /chatRoom
の全取得で権限があるデータだけを取得する、という操作はできないことがわかる。
このことから、 firestore
のデータ構造を以下のようにする必要がある。user
コレクションには、 chatRoom
の情報を非正規化して保持している。ただ、非正規化が行われるということは、チャットルーム名の変更などが発生した場合は、非正規化が発生した部分を全て変更しなければならない。firebaseでは、 collectionGroup()
というメソッドでサブコレクションを串刺しにして取得することができるので、collectionGroup(YYY)
とすることで、非正規化されたデータを一括で書き換えることができるようになる。
(※ user
のサブコレクションの userRoom
を room
にしなかった理由はサブコレクションを串刺しにしたいときに、 room
は他のサブコレクション名とバッティングする恐れがあるため、 userRoom
とした)
Collection | Document | Collection/data | Document | data |
---|---|---|---|---|
chatRoom | XXX | message(C) | ZZZ | |
name(d) | ||||
description(d) | ||||
user | XXX | userRoom(C) | YYY | name(d) |
description(d) |
このようにすることで、チャット一覧の取得は、 /user/{user}/userRoom
で取得するようになる。このとき、認証されたユーザしか /user/{user}/userRoom
のデータにアクセスできない。チャットの詳細を取得するときは、 /chatRoom/{roomId}
にアクセスして、認証されているユーザがこのルームへのアクセス権限があるかを判定している。
match /user/{user}/userRoom { allow read, write: if user == request.auth.uid; } match /chatRoom/{roomId} { allow read, write: if exists(/databases/$(database)/documents/user/$(request.auth.uid)/userRoom/$(roomId)); } // 上記以外全て拒否
これでセキュアな状態(非正規化ではあるもののデータ更新に困らない)のモノになるのではないだろうか。