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
27 changes: 25 additions & 2 deletions kits/storage-resize-images/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,26 @@ function optional(value: string): string | undefined {
return value.length > 0 ? value : undefined;
}

/**
* `ListParam.value()` JSON-parses the raw env var. Extension-style values may
* either throw (for example, `jpeg,webp`) or parse to a non-list and collapse
* to `[]` (notably the extension default, `false`). The extension read the raw
* value directly, so preserve it whenever the params layer cannot return a
* non-empty list and let the resolver's `toArray` apply legacy semantics.
*/
function imageTypesFromEnv(): ReadonlyArray<string> | string | undefined {
const raw = process.env.IMAGE_TYPE;
if (raw === undefined) {
return undefined;
}
try {
const parsed = params.imageTypes.value();
return parsed.length > 0 ? parsed : raw;
} catch {
return raw;
}
}

export function configFromEnv(): ResizeImagesConfig {
return {
bucket: params.bucket.value(),
Expand All @@ -296,11 +316,14 @@ export function configFromEnv(): ResizeImagesConfig {
excludePathList: optional(params.excludePathList.value()),
failedImagesPath: optional(params.failedImagesPath.value()),
cacheControlHeader: optional(params.cacheControlHeader.value()),
imageTypes: params.imageTypes.value(),
imageTypes: imageTypesFromEnv(),
outputOptions: optional(params.outputOptions.value()),
sharpOptions: params.sharpOptions.value(),
isAnimated: params.isAnimated.value(),
memory: params.memory.value(),
// IntParam yields 0 when FUNCTION_MEMORY is unset or non-numeric; the
// extension always supplied a value, so treat that as unset and let the
// resolver fall back to the default memory.
memory: params.memory.value() || undefined,
regenerateToken: params.regenerateToken.value(),
contentFilterLevel:
params.contentFilterLevel.value() as ResizeImagesConfig["contentFilterLevel"],
Expand Down
72 changes: 50 additions & 22 deletions kits/storage-resize-images/tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,53 @@ describe("configFromEnv", () => {
expect(config.projectId).toBe("extensions-testing");
});

// The extension read process.env directly and degraded gracefully against a
// partial environment; the params layer must not turn that into a cold-start
// crash (ListParam JSON-parses IMAGE_TYPE, IntParam yields 0 for
// FUNCTION_MEMORY).
test("survives an unset IMAGE_TYPE", async () => {
delete process.env.IMAGE_TYPE;
const { configFromEnv } = await import("../src/config");
const { resolveResizeImagesConfig } = await import("../src/export-config");

const config = configFromEnv();
expect(config.imageTypes).toBeUndefined();
expect(resolveResizeImagesConfig(config).imageTypes).toEqual(["false"]);
});

test("accepts the extension-style IMAGE_TYPE=false default", async () => {
process.env.IMAGE_TYPE = "false";
const { configFromEnv } = await import("../src/config");
const { resolveResizeImagesConfig } = await import("../src/export-config");

const config = configFromEnv();
expect(config.imageTypes).toBe("false");
expect(resolveResizeImagesConfig(config).imageTypes).toEqual(["false"]);
});

test("accepts an extension-style comma-separated IMAGE_TYPE", async () => {
process.env.IMAGE_TYPE = "jpeg,webp";
const { configFromEnv } = await import("../src/config");
const { resolveResizeImagesConfig } = await import("../src/export-config");

const config = configFromEnv();
expect(config.imageTypes).toBe("jpeg,webp");
expect(resolveResizeImagesConfig(config).imageTypes).toEqual([
"jpeg",
"webp",
]);
});

test("falls back to the default memory when FUNCTION_MEMORY is unset", async () => {
delete process.env.FUNCTION_MEMORY;
const { configFromEnv } = await import("../src/config");
const { resolveResizeImagesConfig } = await import("../src/export-config");

const config = configFromEnv();
expect(config.memory).toBeUndefined();
expect(resolveResizeImagesConfig(config).memory).toBe("1GiB");
});

test("collapses unset optional strings to undefined", async () => {
const { configFromEnv } = await import("../src/config");
const config = configFromEnv();
Expand Down Expand Up @@ -133,6 +180,8 @@ describe("configFromEnv", () => {
// runtime contract. `.value()` reads only `process.env`; the declared
// `default:` is written into the deployed `.env` by the CLI. A hand-rolled
// or partial `.env` therefore yields these values, not the declared ones.
// (memory is the exception: configFromEnv maps IntParam's 0 sentinel to
// undefined so the resolver can apply its default.)
delete process.env.IS_ANIMATED;
delete process.env.REGENERATE_TOKEN;
delete process.env.FUNCTION_MEMORY;
Expand All @@ -143,31 +192,10 @@ describe("configFromEnv", () => {

expect(config.isAnimated).toBe(false);
expect(config.regenerateToken).toBe(false);
expect(config.memory).toBe(0);
expect(config.memory).toBeUndefined();
expect(config.sharpOptions).toBe("");
});

test("a missing IMAGE_TYPE throws instead of falling back to its default", async () => {
// The list param JSON-parses the raw env var, so an absent IMAGE_TYPE is
// a cold-start crash — the kit's counterpart to the extension's
// `IMG_SIZES.split(",")` TypeError on a missing variable.
delete process.env.IMAGE_TYPE;

const { configFromEnv } = await import("../src/config");
expect(() => configFromEnv()).toThrow(SyntaxError);
});

test("IMAGE_TYPE is read as a JSON array, not a comma-separated string", async () => {
// The extension reads the same variable with `.split(",")`, so an
// extension-style value does not carry over.
process.env.IMAGE_TYPE = "jpeg,webp";
const { configFromEnv } = await import("../src/config");
expect(() => configFromEnv()).toThrow(SyntaxError);

process.env.IMAGE_TYPE = '["jpeg","webp"]';
expect(configFromEnv().imageTypes).toEqual(["jpeg", "webp"]);
});

test("reads explicit values for every param", async () => {
Object.assign(process.env, {
IMG_SIZES: "200x200,400x400",
Expand Down
Loading