🚀

Cloud Functions TypeScriptで他のファイルをインポート

2023/04/21に公開

躓いたのでメモしておく

Cloud Functions TypeScriptで、index.tsにロジックを全て書くのはいけてないなと思いました😅
パスマッピングなるものを使えば解決できると思ったがそうではないみたい?
どうしたら、ファイル分けて、デプロイに失敗しないのだろうと、色々試していて、このやり方でやると成功しました。
でも、コマンド二回実行する必要があるかも?

ディレクトリをファイルごとに分ける

今回作ったのは、ユーザーの退会機能と、ログインしている特定のユーザーのコレクションのデータを削除するロジックです。
これが、どんな条件で実行されるかというと、Firestoreに、ユーザーを削除するフラグをつけるコレクションか、フィールドのデータが作られたときに、実行されます。
今回は、そちらは本題ではないので、どうしたらコマンド打って、デプロイをさせてくれるのか、設定をしていきます。
スクリーンショット

削除フラグが作られたら、ユーザーアカウントを削除するトリガー関数

auth/deleteUser
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";

// user account delete
export const deleteUser = functions.region("asia-northeast1").firestore.document("deleted_users/{docId}").onCreate(async (snap, context) => {
  const deleteDocument = snap.data();
  const uid = deleteDocument.uid;

  await admin.auth().deleteUser(uid);
});

削除フラグが作られたら、特定のユーザーコレクションを削除するトリガー関数

firestore/deleteCollection.ts
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";

const db = admin.firestore();

// user collection delete
export const deleteCollection = functions.auth.user().onDelete(async (user) => {
  try {
    // Get the user's UID.
    const uid = user.uid;

    // Delete the user's document from the "user" collection.
    await db.collection("user").doc(uid).delete();

    console.log(`Deleted user document with UID: ${uid}`);
  } catch (error) {
    const uid = user.uid;
    console.error(`Error deleting user document with UID: ${uid}`, error);
  }
});

ここで厄介な問題が出てくるのですが、 admin.initializeApp(); は、1箇所でしか使えません。index.tsに書いて、その下の方に、モダンJavaScriptのimport文と、export文を書いて、他のファイルに書いた関数を読み込みます。

// Initialize the default app
import * as admin from "firebase-admin";
admin.initializeApp();

import { deleteUser } from "./auth/deleteUser";
import { deleteCollection } from "./firestore/deleteCollection";

export { deleteUser, deleteCollection };

そしたら、デブロイができました。

最後に

インターネットで調べていた方法が参考にならなくて、思いつきでやってみたら、できたので、記録を残そうと思って、記事を書くことにしました。

Flutterで削除フラグをつける方法ですが、私はこんな感じのロジックで作りました。参考までにどうぞ💁

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';

class AccountDeletionClass {
  final auth = FirebaseAuth.instance;
  final db = FirebaseFirestore.instance;
  final storage = FirebaseStorage.instance;
  final deletedAt = Timestamp.fromDate(DateTime.now());

  Future<void> deleteAccount() async {
    try {
      final uid = auth.currentUser?.uid;
      const uploadName = 'image.png';

      Map<String, dynamic> data = {
        'uid': uid,
        'deletedAt': deletedAt,
      };

      /// [Cloud Functionsのトリガーを実行する削除フラグをつける]
      await db.collection('deleted_users').doc(uid).set(data);

      /// [FirebaseStorageの画像を削除する]
      await storage.ref().child('image/$uid/$uploadName').delete();

      /// [削除フラグと画像の削除を実行したらログアウトする]
      await auth.signOut();
    } catch (e) {
      throw e.toString();
    }
  }
}

パスマッピングの設定も載せておきます。多分これは、影響していないはず?

tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"]// これですね。
    },
    "module": "commonjs",
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017"
  },
  "compileOnSave": true,
  "include": [
    "src"
  ]
}

Discussion