以发布者的身份将扩展程序迁移到 Cloud Functions(续11)
密钥
在 extension.yaml 中,您可以使用 type: secret 声明 Secret。扩展程序运行时会存储并绑定这些变量,因此您的扩展程序代码可以直接读取 process.env.PARAM_NAME。在典型的 Cloud Functions 代码库中,您需要明确声明和绑定每个 Secret:
import { defineSecret } from "firebase-functions/params";
import { onRequest } from "firebase-functions/https";
const apiKey = defineSecret("API_KEY");
export const fn = onRequest({ secrets: [apiKey] }, handler);
将扩展程序迁移到 npm 软件包/套件后,密钥引用会在最终用户的 .env 文件中进行管理。请务必不要更改代码中声明的 Secret 名称。在迁移期间,最终用户密钥会相应地迁移。
实际操作示例:通过 Cloud Firestore 触发电子邮件
之前。在 config.ts 中,MAIL_COLLECTION 和 SMTP_PASSWORD 读取为原始环境变量:
# extension.yaml
- param: MAIL_COLLECTION
label: Email documents collection
type: string
default: mail
required: true
- param: SMTP_PASSWORD
label: SMTP password
type: secret
// functions/src/config.ts
mailCollection: process.env.MAIL_COLLECTION,
smtpPassword: process.env.SMTP_PASSWORD,
之后。一个 defineString 和一个 defineSecret;CLI 会发现这两个文件并从 .env 中读取:
import { defineString, defineSecret } from "firebase-functions/params";
import { onDocumentWritten } from "firebase-functions/firestore";
const mailCollection = defineString("MAIL_COLLECTION", {
label: "Email documents collection",
default: "mail"
});
const smtpPassword = defineSecret("SMTP_PASSWORD", { label: "SMTP password" });
export const processQueue = onDocumentWritten(
{ document: `${mailCollection}/{documentId}`, secrets: [smtpPassword] },
async (event) => {
const collection = mailCollection.value();
const password = smtpPassword.value();
// ...
}
);
5. 迁移内部任务队列调用
有些扩展程序会使用 Firebase Admin SDK 从其函数代码内部将工作加入到其自己的任务队列中。这与接收已调度的任务(在升级函数和转换生命周期钩子部分中介绍)不同。在此示例中,您的代码是调用 queue.enqueue(...) 的生产者。
在 Admin SDK 的先前版本中,扩展程序需要将其自己的扩展程序实例 ID 作为第二个参数传递,以定位同一扩展程序中的任务队列函数。自 firebase-admin 14.2.0 起,此设置既不是必需的,也不建议使用。任务队列 API 现在默认定位到同一上下文(例如扩展程序或套件)中的任务队列。无论是作为扩展程序还是作为独立函数,您都可以放心地在代码中移除此参数,我们甚至鼓励您这样做。移除此参数可确保可移植性和向前兼容性。
enqueue 调用的其他所有方面(包括 locations/<region>/functions/<name> 资源路径、任务载荷和重试逻辑)都保持不变。
如需详细了解如何使用 Cloud Tasks 将函数加入队列,请参阅使用 Cloud Tasks 将函数加入队列。
之前。第 1 代扩展服务:
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${config.location}/functions/syncBigQuery`,
process.env.EXT_INSTANCE_ID, // extension instance ID, injected by the runtime
);
await queue.enqueue(taskData);
之后。第 2 代扩展服务:
import { getFunctions } from "firebase-admin/functions";
const queue = getFunctions().taskQueue(
`locations/${process.env.FUNCTION_REGION}/functions/syncBigQuery`
);
await queue.enqueue(taskData);
如果您的入队调用以带有前缀的代码库为目标,则发现的函数名称也会带有前缀(例如 orders-syncBigQuery);请参阅查看并安装替换函数套件实例和以函数套件形式进行测试。
6. 声明所需的 API 和 IAM 角色
将扩展程序的 IAM 和 API 要求从 extension.yaml 移到代码中:
import { requiresAPI, requiresRole } from "firebase-functions";
requiresAPI("bigquery.googleapis.com", "Needed to write changelog rows");
requiresRole("roles/bigquery.dataEditor");
requiresRole("roles/bigquery.user");
借助声明式安全性,Firebase CLI 会为代码库创建或更新受管理的运行时服务账号,并向其授予所有已声明角色的并集。向用户说明,代码库中的所有函数都以这些角色运行,除非最终 API 支持更窄的模型。
示例:从 Cloud Firestore 到 BigQuery 的流
之前。在 extension.yaml 中声明;扩展程序运行时已启用该 API 并向受管账号授予了相应角色:
apis:
- apiName: bigquery.googleapis.com
roles:
- role: bigquery.dataEditor
- role: datastore.user
- role: bigquery.user
之后。在代码中使用 requiresAPI 和 requiresRole 声明:
``` import { requiresAPI, requiresRole } from "firebase-functions";
requiresAPI( "bigquery.googleapis.com", "Needed to write changelog rows and views"
