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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"clerk": patch
---

Accept comma-separated values for `--keys` in `config pull` and `config schema`, and clarify that keys refer to top-level config sections.
18 changes: 12 additions & 6 deletions packages/cli-core/src/cli-program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ export function createProgram() {
{ command: "clerk config pull --output config.json", description: "Save config to a file" },
{ command: "clerk config schema", description: "Print full config schema" },
{
command: "clerk config schema --keys social_login",
description: "Schema for specific keys",
command: "clerk config schema --keys auth_email session",
description: "Schema for specific top-level keys",
},
{
command: "clerk config patch --file config.json",
Expand Down Expand Up @@ -309,7 +309,10 @@ export function createProgram() {
.option("--app <id>", "Application ID to target (works from any directory)")
.option("--instance <id>", "Instance to target (dev, prod, or a full instance ID)")
.option("--output <file>", "Write config to a file instead of stdout")
.option("--keys <keys...>", "Config keys to retrieve")
.option(
"--keys <keys...>",
"Top-level config keys to retrieve, separated by spaces or commas (e.g. auth_email session)",
)
.setExamples([
{ command: "clerk config pull", description: "Print dev config to stdout" },
{ command: "clerk config pull --instance prod", description: "Pull production config" },
Expand All @@ -323,12 +326,15 @@ export function createProgram() {
.option("--app <id>", "Application ID to target (works from any directory)")
.option("--instance <id>", "Instance to target (dev, prod, or a full instance ID)")
.option("--output <file>", "Write schema to a file instead of stdout")
.option("--keys <keys...>", "Config keys to retrieve schema for")
.option(
"--keys <keys...>",
"Top-level schema sections to retrieve, separated by spaces or commas (e.g. auth_email session)",
)
.setExamples([
{ command: "clerk config schema", description: "Print full config schema" },
{
command: "clerk config schema --keys social_login",
description: "Schema for specific keys",
command: "clerk config schema --keys auth_email session",
description: "Schema for specific top-level keys",
},
{ command: "clerk config schema --output schema.json", description: "Save schema to a file" },
])
Expand Down
28 changes: 14 additions & 14 deletions packages/cli-core/src/commands/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,17 @@ clerk config pull
clerk config pull --app app_123
clerk config pull --instance prod
clerk config pull --output clerk-config.json
clerk config pull --keys session sign_up
clerk config pull --keys auth_email session
```

#### Options

| Flag | Description |
| ------------------ | ----------------------------------------------------------------------------------- |
| `--app <id>` | Application ID to target directly (works from any directory) |
| `--instance <id>` | Instance to target (`dev`, `prod`, or a full instance ID). Defaults to development. |
| `--output <file>` | Write config to a file instead of stdout |
| `--keys <keys...>` | Config keys to retrieve |
| Flag | Description |
| ------------------ | ------------------------------------------------------------------------------------------ |
| `--app <id>` | Application ID to target directly (works from any directory) |
| `--instance <id>` | Instance to target (`dev`, `prod`, or a full instance ID). Defaults to development. |
| `--output <file>` | Write config to a file instead of stdout |
| `--keys <keys...>` | Top-level config keys to retrieve, separated by spaces or commas (e.g. auth_email session) |

#### Requirements

Expand Down Expand Up @@ -51,17 +51,17 @@ clerk config schema
clerk config schema --app app_123
clerk config schema --instance prod
clerk config schema --output config-schema.json
clerk config schema --keys session sign_up
clerk config schema --keys auth_email session
```

#### Options

| Flag | Description |
| ------------------ | ----------------------------------------------------------------------------------- |
| `--app <id>` | Application ID to target directly (works from any directory) |
| `--instance <id>` | Instance to target (`dev`, `prod`, or a full instance ID). Defaults to development. |
| `--output <file>` | Write schema to a file instead of stdout |
| `--keys <keys...>` | Config keys to retrieve schema for |
| Flag | Description |
| ------------------ | ---------------------------------------------------------------------------------------------- |
| `--app <id>` | Application ID to target directly (works from any directory) |
| `--instance <id>` | Instance to target (`dev`, `prod`, or a full instance ID). Defaults to development. |
| `--output <file>` | Write schema to a file instead of stdout |
| `--keys <keys...>` | Top-level schema sections to retrieve, separated by spaces or commas (e.g. auth_email session) |

#### Requirements

Expand Down
42 changes: 40 additions & 2 deletions packages/cli-core/src/commands/config/pull.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,47 @@ describe("config pull", () => {
instances: { development: "ins_dev" },
});

await runConfigPull({ keys: ["session", "sign_up"] });
await runConfigPull({ keys: ["session", "auth_email"] });
expect(requestedUrl).toContain("keys=session");
expect(requestedUrl).toContain("keys=sign_up");
expect(requestedUrl).toContain("keys=auth_email");
});

test("splits comma-separated --keys into individual params", async () => {
let requestedUrl = "";
stubFetch(async (input) => {
requestedUrl = input.toString();
return new Response(JSON.stringify({ session: { lifetime: 604800 } }), { status: 200 });
});

await setProfile(process.cwd(), {
workspaceId: "org_1",
appId: "app_1",
instances: { development: "ins_dev" },
});

await runConfigPull({ keys: ["session,auth_email"] });
expect(requestedUrl).toContain("keys=session");
expect(requestedUrl).toContain("keys=auth_email");
});

test("trims whitespace from comma-separated --keys", async () => {
let requestedUrl = "";
stubFetch(async (input) => {
requestedUrl = input.toString();
return new Response(JSON.stringify({ session: { lifetime: 604800 } }), { status: 200 });
});

await setProfile(process.cwd(), {
workspaceId: "org_1",
appId: "app_1",
instances: { development: "ins_dev" },
});

await runConfigPull({ keys: ["session, auth_email"] });
expect(requestedUrl).toContain("keys=session");
expect(requestedUrl).toContain("keys=auth_email");
expect(requestedUrl).not.toContain("keys=+");
expect(requestedUrl).not.toContain("keys=%20");
});

test("handles API errors gracefully", async () => {
Expand Down
44 changes: 42 additions & 2 deletions packages/cli-core/src/commands/config/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,52 @@ describe("config schema", () => {
instances: { development: "ins_dev" },
});

await runConfigSchema({ keys: ["session", "sign_up"] });
await runConfigSchema({ keys: ["session", "auth_email"] });
expect(requestedUrl).toContain("keys=session");
expect(requestedUrl).toContain("keys=sign_up");
expect(requestedUrl).toContain("keys=auth_email");
expect(requestedUrl).toContain("/config/schema");
});

test("splits comma-separated --keys into individual params", async () => {
let requestedUrl = "";
stubFetch(async (input) => {
requestedUrl = input.toString();
return new Response(JSON.stringify(mockSchema), { status: 200 });
});

await setProfile(process.cwd(), {
workspaceId: "org_1",
appId: "app_1",
instances: { development: "ins_dev" },
});

await runConfigSchema({ keys: ["session,auth_email"] });
expect(requestedUrl).toContain("/config/schema");
expect(requestedUrl).toContain("keys=session");
expect(requestedUrl).toContain("keys=auth_email");
});

test("trims whitespace from comma-separated --keys", async () => {
let requestedUrl = "";
stubFetch(async (input) => {
requestedUrl = input.toString();
return new Response(JSON.stringify(mockSchema), { status: 200 });
});

await setProfile(process.cwd(), {
workspaceId: "org_1",
appId: "app_1",
instances: { development: "ins_dev" },
});

await runConfigSchema({ keys: ["session, auth_email"] });
expect(requestedUrl).toContain("/config/schema");
expect(requestedUrl).toContain("keys=session");
expect(requestedUrl).toContain("keys=auth_email");
expect(requestedUrl).not.toContain("keys=+");
expect(requestedUrl).not.toContain("keys=%20");
});

test("errors when production instance not configured", async () => {
await setProfile(process.cwd(), {
workspaceId: "org_1",
Expand Down
23 changes: 13 additions & 10 deletions packages/cli-core/src/lib/plapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ async function plapiFetch(method: string, url: URL, init?: { body?: string }): P
return response;
}

/** Normalize and append `keys` query params, splitting comma-separated values. */
function appendKeys(url: URL, keys?: string[]): void {
if (!keys?.length) return;
for (const key of keys) {
for (const k of key.split(",")) {
const trimmed = k.trim();
if (trimmed) url.searchParams.append("keys", trimmed);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}

export async function fetchInstanceConfigSchema(
applicationId: string,
instanceId: string,
Expand All @@ -89,11 +100,7 @@ export async function fetchInstanceConfigSchema(
`/v1/platform/applications/${applicationId}/instances/${instanceId}/config/schema`,
getPlapiBaseUrl(),
);
if (keys?.length) {
for (const key of keys) {
url.searchParams.append("keys", key);
}
}
appendKeys(url, keys);
const response = await plapiFetch("GET", url);
return response.json() as Promise<Record<string, unknown>>;
}
Expand All @@ -107,11 +114,7 @@ export async function fetchInstanceConfig(
`/v1/platform/applications/${applicationId}/instances/${instanceId}/config`,
getPlapiBaseUrl(),
);
if (keys?.length) {
for (const key of keys) {
url.searchParams.append("keys", key);
}
}
appendKeys(url, keys);
const response = await plapiFetch("GET", url);
return response.json() as Promise<Record<string, unknown>>;
}
Expand Down
Loading