🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

2102閲覧

FirebaseのCloud Functionが上手く動かない【多分JavaScriptのミス】

abc1222

総合スコア24

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2020/12/26 02:47

前提・実現したいこと

公式サイトを読みながらCloud Functionのコードを書いてみたのですが、上手く動きません。
下記を実現したくコードを書いたのですが、何処がおかしいのか教えていただけますでしょうか。

・firestoreのデータが変更されたら、Cloud Functionを使い、Custom Claimsを変更したい

JavaScript自体が初めてのため、おそらくfirebase関連は関係なく、JavaScriptのミスだと思います。

該当のソースコード

備考
・firestoreにCircleというコレクションがある
・CircleのフィールドにマスターユーザーのIDを配列として保存している
・ユーザー(Auth)のCustom ClaimsにCircleのIDを配列として保存している
・マスターユーザーが追加(削除)されたら、該当ユーザーのCustom ClaimsにCircleのIDを追加(削除)したい

exports.circleUpdate = functions.region('asia-northeast1').firestore .document('Circle/{circleId}') .onUpdate((change, context) => { const cid = context.params.circleId; const newValue = change.after.data(); const masters = newValue.master; const previousValue = change.before.data(); const previousMasters = previousValue.master; if (masters.length > previousMasters.length) { // マスター追加 const addMasters = masters.filter(i => previousMasters.indexOf(i) == -1); addCustumClamims(addMasters, cid) }else if (masters.length < previousMasters.length) { //マスター削除 const deleteMasters = previousMasters.filter(i => masters.indexOf(i) == -1); deleteCustumClamims(deleteMasters, cid) }else { // マスターに変更なし } }); function addCustumClamims(masters, cid) { masters.forEach(master => { admin.auth().getUser(master).then((userRecord) => { const oldCircle = userRecord.customClaims['circle']; const newCircle = oldCircle.push(cid); admin.auth().setCustomUserClaims(master, {circle: newCircle}).then(() => { console.log("Successfully add master"); }); }); }); } function deleteCustumClamims(masters, cid) { masters.forEach(master =>{ admin.auth().getUser(master).then((userRecord) => { const oldCircle = userRecord.customClaims['circle']; const newCircle = oldCircle.filter(i => i !== cid); admin.auth().setCustomUserClaims(master, {circle: newCircle}).then(() => { console.log("Successfully delete master"); }); }); }); }

試したこと

Cloud Function自体が初めてのため、簡単な処理でそもそもの環境設定?等が間違えてないかは確認済みです。

補足情報(FW/ツールのバージョンなど)

自分なりに調べてJavaScriptの関数の使い方と非同期処理についてが怪しい部分だと思い勉強していますが、原因や治し方が分からないでいます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

先程の回答へのコメントの部分で話したとおり、このトリガー関数では非同期処理を待つ必要があると思います。

動かしてみてはいませんが、以下のようになるのではと思います。

javascript

1const REGION='asia-northeast1'; 2 3/** 4* @param {functions.Change<functions.firestore.QueryDocumentSnapshot>} change 5* @param {*} context 6* @returns {boolean} If update custom user claims 7**/ 8async function onCircleUpdate(change, context) { 9 const cid = context.params.circleId; 10 const newValue = change.after.data(); 11 const masters = newValue.master; 12 const previousValue = change.before.data(); 13 const previousMasters = previousValue.master; 14 15 if (masters.length > previousMasters.length) { 16 // マスター追加 17 const addMasters = masters.filter(i => previousMasters.indexOf(i) == -1); 18 await addCustumClamims(addMasters, cid); 19 return true; 20 }else if (masters.length < previousMasters.length) { 21 // マスター削除 22 const deleteMasters = previousMasters.filter(i => masters.indexOf(i) == -1); 23 await deleteCustumClamims(deleteMasters, cid); 24 return true; 25 }else { 26 // マスターに変更なし 27 return false; 28 } 29 30 } 31 32function addCustumClamims(masters, cid) { 33 return Promise.all(masters.map(async master => { 34 const userRecord = await admin.auth().getUser(master); 35 const oldCircle = userRecord.customClaims['circle']; 36 const newCircle = oldCircle.push(cid); 37 await admin.auth().setCustomUserClaims(master, {circle: newCircle}); 38 console.log("Successfully add master"); 39 })); 40} 41 42function deleteCustumClamims(masters, cid) { 43 return Promise.all(masters.map(async master =>{ 44 const userRecord = await admin.auth().getUser(master); 45 const oldCircle = userRecord.customClaims['circle']; 46 const newCircle = oldCircle.filter(i => i !== cid); 47 await admin.auth().setCustomUserClaims(master, {circle: newCircle}); 48 console.log("Successfully delete master"); 49 })); 50} 51 52// exports triggers 53exports.circleUpdate = functions.region(REGION).firestore 54 .document('Circle/{circleId}') 55 .onUpdate(onCircleUpdate);

投稿2020/12/26 06:05

編集2020/12/26 13:05
aya-eiya

総合スコア97

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

abc1222

2020/12/26 06:12

質問の至らない点が多く、長々とすみません。 別の問題が発生して、頂いたコードが動くかすぐに確認することが出来ないのですが、後は自分の勉強次第で何とか出来ると思います! 本当にありがとうございます! (動かせるようになったら、改めてコメントさせていただきます!)
abc1222

2020/12/26 12:38

index.jsが開けるようになったので、上記のコードを入力してみてのご報告です。 遅くなり申し訳ございません。 コードを上記の通りに変えてみたのですが、デプロイが出来ませんでした。 下記のエラーが発生します。 Error: Error occurred while parsing your function triggers. C:\Users\・・・\Firebase\functions\index.js:75 export const circleCreate = region(REGION).firestore SyntaxError: Unexpected token 'export' index.jsが開けなくなったことと関連していてコードの問題では無いのかもしれませんが、、、 あと1点 function deleteCustumClamims(masters, cid)のPromise.all(masters.forEach(async masterはPromise.all(masters.map(async masterが正しいでしょうか
aya-eiya

2020/12/26 13:03

`export const circleCreate = region(REGION).firestore`というエラーが出ているなら、それが間違いですね。 `exports.circleUpdate = functions.region(REGION).firestore`と上記では書いているはずなので、転記間違いとか確認してみてください。 > function deleteCustumClamims(masters, cid)のPromise.all(masters.forEach(async masterはPromise.all(masters.map(async masterが正しいでしょうか そうです。
abc1222

2020/12/26 13:17

ありがとうございます! 全体のexportsがexportに変更されており、アップデートの後に不用意に何か一括の変更をしてしまっていたみたいです、、、 しょうもないミス申し訳ございません。
abc1222

2020/12/26 13:40

デプロイも出来ました! 長々とありがとうございました!
guest

0

ご質問される際は、”上手く動きません”ではわからないので、

どのようにデプロイしているのか、エミュレータ上では動くのか、実行時のログは取れているのか、Firestoreにはどのようなパスでどのようなドキュメントを追加or編集しているのか。

なども記載すると答えやすいと思います。

それはさておき。

・マスターユーザーが追加(削除)されたら、該当ユーザーのCustom ClaimsにCircleのIDを追加(削除)したい

というときに発火するトリガーがonUpdateで良いのか調べて見てください。

https://firebase.google.com/docs/functions/firestore-events?hl=ja

多分、トリガーの種類が違います。

投稿2020/12/26 03:00

aya-eiya

総合スコア97

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

abc1222

2020/12/26 04:24

ありがとうございます! 記載いただいた内容を追記します! >トリガーが適切か ありがとうございます! 読み直しましたが、onUpdateで間違いないのではないかと思い、詳細を下記に記載します。(私の考え方が間違えている可能性が大なので) 取り急ぎ、Firestoreのパスですが コレクション:Circle ドキュメント:circleID フィールド:”name”: String, ...., "master": [String] 上記の通り、マスターユーザーはCircleのフィールド内に配列として保存しています(Keyがmaster)。 ドキュメントとして追加、削除ではないため、onCreate等では無く、CircleのonUpdateで発火させて、masterに変更があったか、変更があった場合は追加なのか削除なのか配列数で判断するのではないかと考えました。 (onCreate等は、ドキュメントの追加が発火のトリガーだと、読んで認識したのですが、フィールドに対してなのでしょうか) ちなみに、Circle自体を作成する際はCircle作成者、削除時は削除時のマスター全員のCustom ClaimsにcircleIDを追加・削除しないといけないので、onCreateとonDeleteも必要にはなります。 その際にも、addCustumClamims()、deleteCustumClamims()は共通の処理になると思い、関数として用意しました。 ただ、上記が動かないので、そちらには手を付けられていません(ご指摘いただいた通り、動かない理由をまずは追記します)
aya-eiya

2020/12/26 04:52

なるほど、masterの追加、削除とは、配列型のフィールドに対する追加、削除ということですね。 であれば、トリガーはonUpdateで間違いないと思います。 内容が読めておらず失礼しました。 ”動かない”は曖昧な日本語なので ドキュメントの更新自体ができているのか トリガーが発火しているのか エラーで落ちてしまうのか 想定している処理がどこまで進んだのか、その確認方法や確認した出力があると調査がしやすいと思います。 例えば、指定したCircle/{circleID}について、ドキュメントが想定どおり更新されていて、 i functions: Beginning execution of "circleUpdate" とログに出ているのでトリガーは起動していると考えているだとか、そういうことです。
abc1222

2020/12/26 05:20

ご確認いただき、ありがとうございます。 Cloud Functionが初めて故、読んで認識した内容自体が間違えている可能性もあり、ご指摘いただいたことで、onUpdate等の認識自体は正しかったことが確認できただけでも前進です。 >”動かない”は曖昧な日本語 更に詳しくありがとうございます! 頂いた内容を記載しようと触り直してた所、別の問題(index.jsが開けなくなった)が発生したので、少々お待ちください。 多分、質問しながら自分なりに解決策を模索している中で、「最新の CLI バージョンに更新する」という作業をしたのが良くなかったみたいで、、、 申し訳ありません。 うろ覚えですが、トリガーは発火したのですが、その後、失敗と再試行を繰り返していたと思います。 予定外のエラーで取り急ぎのエラー内容の報告で申し訳ありません。 自分で調べている中で、上記はreturnをどこにも記載していない場合に起きるという感じの記載を見つけて、JavaScriptの関数の使い方と非同期処理について調べだした経緯があるので、多分あってると思いますが。。。
abc1222

2020/12/26 05:30

>returnをどこにも記載していない場合に起きるという感じの記載を見つけ どこで見つけたのか、サイトが分かったので、取り急ぎ追記しておきます。記載内容と同じ現象だったと思うので。 https://firebase.google.com/docs/functions/get-started?hl=ja Cloud Firestore イベントなどのイベント ドリブンの関数は非同期です。コールバック関数は、null、オブジェクト、Promise のいずれかを返す必要があります。何も返さない場合、関数はタイムアウトし、エラーを通知し、再試行されます。同期、非同期、Promise をご覧ください。
aya-eiya

2020/12/26 06:10 編集

なるほど、だいぶわかりました。 注意したい点は2つ。 1. `addCustumClamims`の中で、mastersをforeachでループさせていますが、このループの中で非同期処理を呼んでいますが、foreachの自体はそれらを待たずに終了します。 そのため、foreachの代わりにmastersをPromiseに変換するmapを使って、Promiseの配列を作り、`addCustumClamims`はPromise配列の非同期処理が全て終わった時点で終了となるようにしなければなりません。つまり`addCustumClamims`もPromiseを返す必要があります。この処理にはPromise.all()を使うと便利でしょう。 2.トリガー関数では確かに値を返す必要があります。今回は非同期処理を待たなければならないので、トリガー関数はPromiseを返す必要があります。`addCustumClamims`が返したPromiseをそのまま返すか、エラーなりの情報を含んだPromiseにマップし直して返すとよいでしょう。 なお、非同期処理が多いので、Nodeのバージョンやビルドのルールが許せばasync-awaitを覚えて書き直したほうが見通しが良いでしょう。
abc1222

2020/12/26 06:01

ありがとうございます! 今index.jsが開けないので、実際に書くことが出来ないのですが、原因が分かって良かったです! 非同期処理自体が勉強し始めなので実際のコードがどうなるのかはイメージ出来ていないのですが、頂いた通りに出来るよう弄ってみます! (async-awaitの方を勉強してしまっていて、どうにも良く分からないなと思っていたのですが、Promiseを勉強して仕組みから理解するようにしてみます!)
aya-eiya

2020/12/26 06:06

ベストアンサーありがとうございます。 一応別の回答でコードを書いて見ました。
abc1222

2020/12/26 06:14

コードまでありがとうございます! 今後同じ問題にぶつかった方のために、ベストアンサーをコードの方に変更しました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問