Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/tasty-rockets-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": minor
---

Add jurisdiction support to d1 db creation via command-line argument
53 changes: 53 additions & 0 deletions packages/wrangler/src/__tests__/d1/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,57 @@ describe("create", () => {
}"
`);
});

it("should fail if the jurisdiction provided is not supported", async () => {
writeWranglerConfig({ name: "worker" }, "wrangler.json");

await expect(runWrangler("d1 create test --jurisdiction something")).rejects
.toThrowErrorMatchingInlineSnapshot(`
[Error: Invalid values:
Argument: jurisdiction, Given: "something", Choices: "eu", "fedramp"]
`);
});

it("should try send jurisdiction to the API if it is a valid input", async () => {
writeWranglerConfig({ name: "worker" }, "wrangler.json");

setIsTTY(false);
mockGetMemberships([
{ id: "IG-88", account: { id: "1701", name: "enterprise" } },
]);
msw.use(
http.post("*/accounts/:accountId/d1/database", async () => {
return HttpResponse.json({
result: {
uuid: "51e7c314-456e-4167-b6c3-869ad188fc23",
name: "test",
created_in_region: "WEUR",
jurisdiction: "eu",
},
success: true,
errors: [],
messages: [],
});
})
);
await runWrangler("d1 create test --jurisdiction eu --binding MY_TEST_DB");
expect(std.out).toMatchInlineSnapshot(`
"
⛅️ wrangler x.x.x
──────────────────
✅ Successfully created DB 'test' in region WEUR
Created your new D1 database.

To access your new D1 Database in your Worker, add the following snippet to your configuration file:
{
\\"d1_databases\\": [
{
\\"binding\\": \\"MY_TEST_DB\\",
\\"database_name\\": \\"test\\",
\\"database_id\\": \\"51e7c314-456e-4167-b6c3-869ad188fc23\\"
}
]
}"
`);
});
});
1 change: 1 addition & 0 deletions packages/wrangler/src/d1/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const DEFAULT_MIGRATION_PATH = "./migrations";
export const DEFAULT_MIGRATION_TABLE = "d1_migrations";
export const LOCATION_CHOICES = ["weur", "eeur", "apac", "oc", "wnam", "enam"];
export const JURISDICTION_CHOICES = ["eu", "fedramp"];
114 changes: 70 additions & 44 deletions packages/wrangler/src/d1/create.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import dedent from "ts-dedent";
import { fetchResult } from "../cfetch";
import { formatConfigSnippet, withConfig } from "../config";
import { sharedResourceCreationArgs, updateConfigFile } from "../config";
import { createCommand } from "../core/create-command";
import { getD1ExtraLocationChoices } from "../environment-variables/misc-variables";
import { UserError } from "../errors";
import { logger } from "../logger";
import { requireAuth } from "../user";
import { printWranglerBanner } from "../wrangler-banner";
import { LOCATION_CHOICES } from "./constants";
import type {
CommonYargsArgv,
StrictYargsOptionsToInterface,
} from "../yargs-types";
import { getValidBindingName } from "../utils/getValidBindingName";
import { JURISDICTION_CHOICES, LOCATION_CHOICES } from "./constants";
import type { ComplianceConfig } from "../environment-variables/misc-variables";
import type { DatabaseCreationResult } from "./types";

export async function createD1Database(
complianceConfig: ComplianceConfig,
accountId: string,
name: string,
location?: string
location?: string,
jurisdiction?: string
) {
try {
return await fetchResult<DatabaseCreationResult>(
complianceConfig,
`/accounts/${accountId}/d1/database`,
{
method: "POST",
Expand All @@ -27,6 +30,7 @@ export async function createD1Database(
body: JSON.stringify({
name,
...(location && { primary_location_hint: location }),
...(jurisdiction && { jurisdiction }),
}),
}
);
Expand All @@ -39,37 +43,57 @@ export async function createD1Database(
}
}

export function Options(yargs: CommonYargsArgv) {
return yargs
.positional("name", {
describe: "The name of the new DB",
export const d1CreateCommand = createCommand({
metadata: {
description: "Create D1 database",
status: "stable",
owner: "Product: D1",
},
args: {
name: {
type: "string",
demandOption: true,
})
.option("location", {
describe:
"A hint for the primary location of the new DB. Options:\nweur: Western Europe\neeur: Eastern Europe\napac: Asia Pacific\noc: Oceania\nwnam: Western North America\nenam: Eastern North America \n",
description: "The name of the new DB",
},
location: {
type: "string",
});
}
choices: [
...LOCATION_CHOICES,
...(getD1ExtraLocationChoices()?.split(",") ?? []),
],
description: dedent`
A hint for the primary location of the new DB. Options:
weur: Western Europe
eeur: Eastern Europe
apac: Asia Pacific
oc: Oceania
wnam: Western North America
enam: Eastern North America

type HandlerOptions = StrictYargsOptionsToInterface<typeof Options>;
export const Handler = withConfig<HandlerOptions>(
async ({ name, config, location }): Promise<void> => {
await printWranglerBanner();
`,
},
jurisdiction: {
type: "string",
choices: [...JURISDICTION_CHOICES],
description: dedent`
The location to restrict the D1 database to run and store data within to comply with local regulations. Note that if jurisdictions are set, the location hint is ignored. Options:
eu: The European Union
fedramp: FedRAMP-compliant data centers
`,
},
...sharedResourceCreationArgs,
},
positionalArgs: ["name"],
async handler({ name, location, jurisdiction, env, ...args }, { config }) {
const accountId = await requireAuth(config);

if (location) {
if (LOCATION_CHOICES.indexOf(location.toLowerCase()) === -1) {
throw new UserError(
`Location '${location}' invalid. Valid values are ${LOCATION_CHOICES.join(
","
)}`
);
}
}

const db = await createD1Database(accountId, name, location);
const db = await createD1Database(
config,
accountId,
name,
location,
jurisdiction
);

logger.log(
`✅ Successfully created DB '${db.name}'${
Expand All @@ -81,15 +105,17 @@ export const Handler = withConfig<HandlerOptions>(
}`
);
logger.log("Created your new D1 database.\n");
logger.log(
formatConfigSnippet(
{
d1_databases: [
{ binding: "DB", database_name: db.name, database_id: db.uuid },
],
},
config.configPath
)

await updateConfigFile(
"d1_databases",
(bindingName) => ({
binding: getValidBindingName(bindingName ?? db.name, "DB"),
database_name: db.name,
database_id: db.uuid,
}),
config.configPath,
env,
args
);
}
);
},
});
Loading