Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions kits/delete-user-data/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- fix: `ENABLE_AUTO_DISCOVERY` accepts the extension's `yes` / `no` values again. The kit had declared it as a boolean param, which only counts the literal `true` as enabled, so a `.env` copied from an installed extension instance carried `yes` and silently left auto-discovery switched off. The param is now the extension's labeled `Yes` / `No` select and is read the same way the extension read it.
- Initial release of kit, see README for differences between the legacy extension and this kit
- The instance id now comes from `FIREBASE_KIT_INSTANCE_ID`, which the Firebase CLI (15.27.0 or later) provides to each kit instance; `INSTANCE_ID` is no longer a configuration parameter
9 changes: 1 addition & 8 deletions kits/delete-user-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ overridden there.
| `rtdbPaths` | `RTDB_PATHS` | no | (empty) | Comma-separated RTDB paths with `{UID}` |
| `storageBucket` | `CLOUD_STORAGE_BUCKET` | no | default Storage bucket | Bucket to clear |
| `storagePaths` | `STORAGE_PATHS` | no | (empty) | Comma-separated Storage paths with `{UID}` |
| `enableAutoDiscovery` | `ENABLE_AUTO_DISCOVERY` | no | `false` | Auto-discover user-linked docs |
| `enableAutoDiscovery` | `ENABLE_AUTO_DISCOVERY` | no | `no` | Auto-discover user-linked docs (`yes` or `no`) |
| `searchDepth` | `AUTO_DISCOVERY_SEARCH_DEPTH` | no | `3` | Discovery depth |
| `searchFields` | `AUTO_DISCOVERY_SEARCH_FIELDS` | no | `id,uid,userId` | Fields treated as user ids |
| `searchFunction` | `SEARCH_FUNCTION` | no | (empty) | Optional custom search function |
Expand Down Expand Up @@ -147,13 +147,6 @@ This kit is the extension repackaged as an npm package, but a few things behave
differently. If you are moving from an installed extension instance, read this
section before you deploy.

### Auto-discovery uses `true` / `false`

`ENABLE_AUTO_DISCOVERY` is a boolean param, and only the literal string `true`
enables it. The extension used `yes` / `no`, so copying an old config across
leaves auto-discovery silently switched off. Change `yes` to `true` in your
`.env`.

### The instance id comes from `firebase.json`

The extension derived an instance id at install time and used it to name the
Expand Down
8 changes: 4 additions & 4 deletions kits/delete-user-data/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/

import {
defineBoolean,
defineInt,
defineString,
type IntParam,
Expand Down Expand Up @@ -116,12 +115,13 @@ const params = {
},
},
}),
enableAutoDiscovery: defineBoolean("ENABLE_AUTO_DISCOVERY", {
enableAutoDiscovery: defineString("ENABLE_AUTO_DISCOVERY", {
label: "Enable auto discovery",
description:
"Enable the extension to automatically discover Firestore collections and documents to delete.",

default: false,
default: "no",
input: select({ Yes: "yes", No: "no" }),
}),
searchDepth: defineInt("AUTO_DISCOVERY_SEARCH_DEPTH", {
label: "Auto discovery search depth",
Expand Down Expand Up @@ -197,7 +197,7 @@ export function configFromEnv(): DeleteUserDataConfig {
storageBucket:
optional(params.storageBucket.value()) ?? process.env.STORAGE_BUCKET,
storagePaths: optional(params.storagePaths.value()),
enableAutoDiscovery: params.enableAutoDiscovery.value(),
enableAutoDiscovery: params.enableAutoDiscovery.value() === "yes",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: bare === "yes" is exact parity with the extension, so fine to keep. Just flagging that #3145 landed a trimming/lowercasing yesNo() helper in bigquery-export (src/config.ts:640) for the same shape, so we now have two conventions. Not for this PR, but I think we should pick one and apply it everywhere in a follow-up, otherwise a hand-edited YES behaves differently per kit. Same applies to the two in chatbot config.ts:377-378.

searchDepth: optionalInt(params.searchDepth),
searchFields: params.searchFields.value(),
searchFunction: optional(params.searchFunction.value()),
Expand Down
41 changes: 34 additions & 7 deletions kits/delete-user-data/tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class FakeStringParam extends FakeExpression<string> {
}

value(): string {
if (process.env[this.name] !== undefined) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: reading process.env first in FakeStringParam.value() changes what every string param in this suite resolves to, not just the new one. CI is green so nothing regressed, but worth a look that no other test here was silently relying on the default path.

return process.env[this.name];
}
if (this.defaultValue instanceof FakeStringParam) {
return this.defaultValue.value();
}
Expand All @@ -54,8 +57,13 @@ const defineInt = vi.fn((name: string, opts?: { default?: number }) => ({
value: () => opts?.default ?? 0,
}));

const defineBoolean = vi.fn((_name: string, opts?: { default?: boolean }) => ({
value: () => opts?.default ?? false,
const select = vi.fn((options: Record<string, string>) => ({
select: {
options: Object.entries(options).map(([label, value]) => ({
label,
value,
})),
},
}));

function cel(value: unknown): string {
Expand All @@ -64,19 +72,18 @@ function cel(value: unknown): string {

vi.mock("firebase-functions/params", () => ({
Expression: FakeExpression,
defineBoolean,
defineInt,
defineString,
projectID: { value: () => "demo-test" },
select: vi.fn((options: string[]) => ({ options })),
select,
storageBucket: new FakeStringParam("STORAGE_BUCKET", "demo-test.appspot.com"),
}));

async function importConfig() {
vi.resetModules();
defineString.mockClear();
defineInt.mockClear();
defineBoolean.mockClear();
select.mockClear();
vi.stubEnv("FIREBASE_KIT_INSTANCE_ID", "test-instance");

return import("../src/config");
Expand Down Expand Up @@ -112,6 +119,16 @@ describe("configFromEnv", () => {
expect(config.searchDepth).toBeUndefined();
});

test("parses the predecessor's yes/no values", async () => {
const { configFromEnv } = await importConfig();

vi.stubEnv("ENABLE_AUTO_DISCOVERY", "yes");
expect(configFromEnv().enableAutoDiscovery).toBe(true);

vi.stubEnv("ENABLE_AUTO_DISCOVERY", "no");
expect(configFromEnv().enableAutoDiscovery).toBe(false);
});

test("declares the params the extension exposes", async () => {
await importConfig();

Expand All @@ -136,9 +153,19 @@ describe("configFromEnv", () => {
"AUTO_DISCOVERY_SEARCH_DEPTH",
expect.objectContaining({ default: 3 }),
]);
expect(defineBoolean.mock.calls).toContainEqual([
expect(defineString.mock.calls).toContainEqual([
"ENABLE_AUTO_DISCOVERY",
expect.objectContaining({ default: false }),
expect.objectContaining({
default: "no",
input: {
select: {
options: [
{ label: "Yes", value: "yes" },
{ label: "No", value: "no" },
],
},
},
}),
]);
});

Expand Down
1 change: 1 addition & 0 deletions kits/firestore-genai-chatbot/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
- fix: `ENABLE_DISCUSSION_OPTION_OVERRIDES` and `ENABLE_GENKIT_MONITORING` accept the extension's `yes` / `no` values again. The kit had declared them as boolean params, which only count the literal `true` as enabled, so a `.env` copied from an installed extension instance carried `yes` and silently left per-discussion overrides and Genkit monitoring off. Both are now the extension's labeled `Yes` / `No` selects and are read the same way the extension read them.
- Initial release of kit, see README for differences between the legacy extension and this kit
17 changes: 5 additions & 12 deletions kits/firestore-genai-chatbot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ the CLI connects them to the function at deploy time.
| `topK` | `TOP_K` | no | (empty) | Top-k |
| `candidateCount` | `CANDIDATE_COUNT` | no | `1` | Candidate count |
| `maxOutputTokens` | `MAX_OUTPUT_TOKENS` | no | (empty) | Max output tokens |
| `enableOverrides` | `ENABLE_DISCUSSION_OPTION_OVERRIDES` | no | `false` | Per-discussion option overrides |
| `enableGenkitMonitoring` | `ENABLE_GENKIT_MONITORING` | no | `false` | Enable Genkit monitoring |
| `enableOverrides` | `ENABLE_DISCUSSION_OPTION_OVERRIDES` | no | `no` | Per-discussion option overrides (`yes` or `no`) |
| `enableGenkitMonitoring` | `ENABLE_GENKIT_MONITORING` | no | `no` | Enable Genkit monitoring (`yes` or `no`) |
| `harmHateSpeech` | `HARM_CATEGORY_HATE_SPEECH` | no | `HARM_BLOCK_THRESHOLD_UNSPECIFIED` | Harm threshold |
| `harmDangerous` | `HARM_CATEGORY_DANGEROUS_CONTENT` | no | `HARM_BLOCK_THRESHOLD_UNSPECIFIED` | Harm threshold |
| `harmHarassment` | `HARM_CATEGORY_HARASSMENT` | no | `HARM_BLOCK_THRESHOLD_UNSPECIFIED` | Harm threshold |
Expand Down Expand Up @@ -141,16 +141,9 @@ to your own functions codebase. The generation logic, the Firestore trigger, the
`status` state machine, the per-discussion overrides and the safety settings are
all ported verbatim. Config keeps the same environment variable names, so a
`.env` copied from your installed instance is close to a lift-and-shift, with
four exceptions below: the boolean toggles, the two region settings, and the API
key secret.

### Change `yes` and `no` to `true` and `false`

`ENABLE_DISCUSSION_OPTION_OVERRIDES` and `ENABLE_GENKIT_MONITORING` were
`yes`/`no` dropdowns. They are now booleans that count as enabled only for the
exact value `true`. A copied `.env` carrying `yes` deploys without complaint and
silently leaves the feature off, so per-discussion overrides stop being read and
Genkit monitoring stops reporting.
the exceptions below: the two region settings and the API key secret.
`ENABLE_DISCUSSION_OPTION_OVERRIDES` and `ENABLE_GENKIT_MONITORING` keep the
extension's `yes` / `no` values.

### Pick your Cloud Functions region, or you get us-central1

Expand Down
15 changes: 8 additions & 7 deletions kits/firestore-genai-chatbot/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/

import {
defineBoolean,
defineSecret,
defineString,
expr,
Expand Down Expand Up @@ -243,19 +242,21 @@ const params = {
default: "",
input: POSITIVE_INT_VALIDATION,
}),
enableOverrides: defineBoolean("ENABLE_DISCUSSION_OPTION_OVERRIDES", {
enableOverrides: defineString("ENABLE_DISCUSSION_OPTION_OVERRIDES", {
label: "Enable per document overrides.",
description:
'If set to "Yes", discussion parameters may be overwritten by fields in the discussion collection.',

default: false,
default: "no",
input: select({ Yes: "yes", No: "no" }),
}),
enableGenkitMonitoring: defineBoolean("ENABLE_GENKIT_MONITORING", {
enableGenkitMonitoring: defineString("ENABLE_GENKIT_MONITORING", {
label: "Enable Genkit Monitoring",
description:
'If set to "Yes", enables Genkit Monitoring for collecting and viewing real-time telemetry data. This requires the Cloud Logging API, Cloud Trace API, and Cloud Monitoring API to be enabled, and appropriate IAM roles to be configured. See the documentation for more details.',

default: false,
default: "no",
input: select({ Yes: "yes", No: "no" }),
}),
harmHateSpeech: defineString("HARM_CATEGORY_HATE_SPEECH", {
label: "Hate Speech Threshold",
Expand Down Expand Up @@ -373,8 +374,8 @@ export function configFromEnv(): GenaiChatbotConfig {
topK: num(params.topK.value()),
candidateCount: num(params.candidateCount.value()),
maxOutputTokens: num(params.maxOutputTokens.value()),
enableOverrides: params.enableOverrides.value(),
enableGenkitMonitoring: params.enableGenkitMonitoring.value(),
enableOverrides: params.enableOverrides.value() === "yes",
enableGenkitMonitoring: params.enableGenkitMonitoring.value() === "yes",
safetySettings: buildSafetySettings(),
secrets: [apiKeySecret],
};
Expand Down
111 changes: 111 additions & 0 deletions kits/firestore-genai-chatbot/tests/config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Compatibility requirement, not aspiration: the extension declared these
* toggles as `yes` / `no` selects, so a deployed `.env` copied from an
* installed instance carries those literal values.
*/

import { declaredParams } from "firebase-functions/params";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { configFromEnv } from "../src/config";

function declaration(name: string) {
const param = declaredParams.find((candidate) => candidate.name === name);
if (!param || !("options" in param)) {
throw new Error(`Missing declaration for ${name}`);
}
const options = param.options as { default?: unknown; input?: unknown };
return {
type: (param.constructor as unknown as { type: string }).type,
default: options.default,
input: options.input,
};
}

const KEYS = [
"ENABLE_DISCUSSION_OPTION_OVERRIDES",
"ENABLE_GENKIT_MONITORING",
"FIREBASE_CONFIG",
] as const;

describe("select values inherited from the extension", () => {
const saved = new Map<string, string | undefined>();

test("declares the predecessor's labeled string selects", () => {
for (const name of [
"ENABLE_DISCUSSION_OPTION_OVERRIDES",
"ENABLE_GENKIT_MONITORING",
]) {
expect(declaration(name)).toEqual({
type: "string",
default: "no",
input: {
select: {
options: [
{ label: "Yes", value: "yes" },
{ label: "No", value: "no" },
],
},
},
});
}
});

beforeEach(() => {
for (const key of KEYS) {
saved.set(key, process.env[key]);
delete process.env[key];
}
// The project id resolves out of the runtime-injected FIREBASE_CONFIG.
process.env.FIREBASE_CONFIG = JSON.stringify({ projectId: "demo-test" });
});

afterEach(() => {
for (const [key, value] of saved) {
if (value === undefined) {
delete process.env[key];
} else {
process.env[key] = value;
}
}
saved.clear();
});

test("reads ENABLE_DISCUSSION_OPTION_OVERRIDES as yes/no", () => {
process.env.ENABLE_DISCUSSION_OPTION_OVERRIDES = "yes";
expect(configFromEnv().enableOverrides).toBe(true);

process.env.ENABLE_DISCUSSION_OPTION_OVERRIDES = "no";
expect(configFromEnv().enableOverrides).toBe(false);
});

test("reads ENABLE_GENKIT_MONITORING as yes/no", () => {
process.env.ENABLE_GENKIT_MONITORING = "yes";
expect(configFromEnv().enableGenkitMonitoring).toBe(true);

process.env.ENABLE_GENKIT_MONITORING = "no";
expect(configFromEnv().enableGenkitMonitoring).toBe(false);
});

test("treats an unset variable as off, as the extension did", () => {
const config = configFromEnv();

expect(config.enableOverrides).toBe(false);
expect(config.enableGenkitMonitoring).toBe(false);
});
});
1 change: 1 addition & 0 deletions kits/firestore-send-email/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- fix: restore the extension's `Yes` / `No` option labels on the `OAUTH_SECURE` deploy-time prompt. The stored values are unchanged (`true`/`false`), so this is a label-only fix and no `.env` from an earlier deploy needs editing.
- chore: run on firebase-functions ^7.3.3-rc.0, the same release candidate as the other kits
- fix: map `DATABASE_REGION` to a valid Cloud Run region before using it as the function region. Firestore multi-region locations (`nam5`, `nam7`) now deploy the function to `us-central1` and `eur3` to `europe-west1` instead of failing the deploy; regional locations pass through unchanged. The value is matched case-insensitively. With the parameter unset or empty the function declares no region and the Firebase CLI resolves one at deploy time.
- Initial release of kit, see README for differences between the legacy extension and this kit
Expand Down
4 changes: 2 additions & 2 deletions kits/firestore-send-email/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,8 @@ a `TypeError` about reading `attachments` into `delivery.error`. It now writes
path pattern so nested collections such as `users/{uid}/mail` keep working, and
`MAIL_COLLECTION` still defaults to `mail`.
- Every environment variable keeps its name, type and default, including
`OAUTH_SECURE`, which was a `true`/`false` dropdown and is now a boolean that
reads those same two values.
`OAUTH_SECURE`, which is still a `true`/`false` dropdown, and keeps the
extension's `Yes` / `No` option labels.
- Document fields and their meanings are identical: `to`, `cc`, `bcc`, the
`*Uids` variants, `message`, `template`, `sendGrid`, `headers`, `categories`,
`from` and `replyTo`, along with the validation error messages written to
Expand Down
1 change: 1 addition & 0 deletions kits/firestore-send-email/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ const params = {
"Set to true to enable a secure connection (TLS/SSL) when using OAuth2 authentication for the SMTP server.",

default: true,
input: select({ Yes: true, No: false }),
}),
clientId: defineSecret("CLIENT_ID", {
label: "OAuth2 Client ID",
Expand Down
Loading
Loading