NodeJS 如何将测试数据从生产环境导入Firebase Firestore仿真器以方便测试?

uelo1irk  于 2023-05-28  发布在  Node.js
关注(0)|答案(4)|浏览(307)

我正在构建一个firebase函数,它可以监听触发器并向用户发送推送通知。触发器是基于firestore数据,这对我来说太复杂了,每次都要在firestore模拟器上手动重新创建。
我尝试模拟函数,但连接到firestore生产,但似乎在这种情况下,触发器函数不工作😢
我想做的是从我的生产Firestore中导出我的数据,然后将其导入到我的模拟器Firestore中,这样至少,我正在使用的副本与我在prod上使用的副本非常相似;需要说明的是,数据量很小,所以我并不担心下载TB级的数据。
我找到了一种将数据导入模拟器的方法,但不确定这是否适用于生产数据,也不确定如何从生产中转储数据。

firebase emulators:start --import=./some-directory
ttp71kqs

ttp71kqs1#

您可以手动从生产中导出,然后导入到Firestore模拟器中。
首先将生产数据导出到文件。在本例中,我只在onRequest函数中导出“users”集合,这样就可以通过调用URL来启动。注意,这个函数不会处理子集合。

exports.exportFirestore = functions.https.onRequest(async (req, res) => {
  const fs = require("fs");

  const collection = "users";
  const fileName = "fs-export.json";
  const exportedData = {};
  exportedData[collection] = {};

  await admin
    .firestore()
    .collection(collection)
    .get()
    .then((snapshot) => {
      return snapshot.forEach((doc) => {
        exportedData[collection][doc.id] = doc.data();
      });
    })
    .catch(console.error);

  fs.writeFile(fileName, JSON.stringify(exportedData), (err) => {
    if (err) {
      console.log(err);
    } else {
      console.log("Firestore Export Complete.");
      res.send("Firestore Export Complete.");
    }
  });
});

接下来,将文件导入本地Firestore仿真器。**非常重要:**一定要有Firestore模拟器运行,否则你会导入你的数据回prod.确保通过检查Web UI检查Firestore是否正在运行:http://localhost:4000。使用以下命令启动模拟器:firebase emulators:start

exports.importFirestore = functions.https.onRequest(async (req, res) => {
  const fs = require("fs");

  let collection;
  const fileName = "fs-export.json";
  const exportedData = {};
  exportedData[collection] = {};

  fs.readFile(fileName, "utf8", async (err, data) => {
    if (err) {
      return console.log(err);
    }

    const arr = JSON.parse(data);

    const batch = admin.firestore().batch();

    for (let i in arr) {
      collection = i;
      for (let doc in arr[i]) {
        if (arr[i].hasOwnProperty(doc)) {
          const ref = admin.firestore().collection(collection).doc(doc);
          batch.set(ref, arr[i][doc]);
        } else {
          console.log("Missing:", JSON.stringify(doc, null, 2));
        }
      }
    }

    await batch
      .commit()
      .then(() => {
        return console.log("Import to Firestore Complete");
      })
      .catch(console.error);

    return res.send("Import to Firestore Complete");
  });
});
zy1mlcev

zy1mlcev2#

持久化数据的一种方法是在退出时导出到目录,并在启动时导入相同的目录

firebase emulators:start  --import=./mydir --export-on-exit mydir
pqwbnv8z

pqwbnv8z3#

我的答案很大程度上受到了@Geoffrey Bourne的答案的启发,但我不得不修改一些东西,并找出更多的细节来让它工作。
首先,我将exportFirestore上传到Cloud Functions(生产)。当我通过这个URL https://us-central1-<project-id>.cloudfunctions.net/exportFirestore运行它时,我得到一个文件DOWNLOADED,因为云函数是read-only
下面的代码是一个名为fl_content的集合,我将考虑将其扩展到多个集合

export const exportFirestore = functions.https.onRequest(async (req, res) => {
    const collection = "fl_content";
    const exportedData: any = {};
    exportedData[collection] = {};

    await admin
        .firestore()
        .collection(collection)
        .get()
        .then((snapshot) => snapshot.forEach((doc) => exportedData[collection][doc.id] = doc.data()))
        .catch(console.error);

    const data = JSON.stringify(exportedData);
    res.setHeader('Content-disposition', 'attachment; filename=fire-export.json');
    res.setHeader('Content-type', 'application/json');
    res.write(data, function () {
        res.end();
    });
})

下载文件fire-export.json后,将其放入functions文件夹。然后打开导入函数(本地)http://localhost:5001/<project-id>/us-central1/importFirestore的URL。确保collection变量在导出和导入中相同。

export const importFirestore = functions.https.onRequest(async (req, res) => {
    const fs = require("fs");
    const collection = "fl_content";
    const fileName = "fire-export.json";
    const exportedData: any = {};
    exportedData[collection] = {};

    fs.readFile(fileName, "utf8", async (err: any, data: any) => {
        if (err) {
            res.send(err);
            functions.logger.error(err)
            return;
        }
        const arr = JSON.parse(data);
        const batch = admin.firestore().batch();

        for (const i in arr) {
            for (const doc in arr[i]) {
                if (arr[i].hasOwnProperty(doc)) {
                    const ref = admin.firestore().collection(collection).doc(doc);
                    batch.set(ref, arr[i][doc]);
                } else {
                    functions.logger.error("Missing:", JSON.stringify(doc, null, 2));
                }
            }
        }

        await batch
            .commit()
            .then(() => console.log("Import to Firestore Complete"))
            .catch(console.error);

        res.send("Import to Firestore Complete");
    });
});
yzuktlbb

yzuktlbb4#

您可以使用gcloud(Google Cloud的CLI)来执行此操作。它需要几个命令:
首先,创建Firestore staging(使用生产数据库进行本地开发可能有潜在的危险)数据库的firestore导出(首先确保此存储桶存在!):

gcloud firestore export gs://{BUCKET}/{DIRECTORY}

然后,从这个bucket下载数据:

gsutil -m cp -r gs://{BUCKET}/{DIRECTORY} ./{LOCAL_DIRECTORY_NAME}

最后,使用以下命令运行模拟器:

firebase emulators:start --import ./{LOCAL_DIRECTORY_NAME}

这将为您提供一个临时数据库的本地副本。

相关问题