Skip to content

Commit

Permalink
feat(wrangler): identify draft and inherit bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
edmundhung committed Dec 3, 2024
1 parent 2eed4b3 commit 04b5b5b
Show file tree
Hide file tree
Showing 5 changed files with 344 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changeset/swift-bulldogs-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": patch
---

The `x-provision` experimental flag now identifies draft and inherit bindings by looking up the current binding settings.
151 changes: 148 additions & 3 deletions packages/wrangler/src/__tests__/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { writeWranglerConfig } from "./helpers/write-wrangler-config";
import type { AssetManifest } from "../assets";
import type { Config } from "../config";
import type { CustomDomain, CustomDomainChangeset } from "../deploy/deploy";
import type { Settings } from "../deployment-bundle/bindings";
import type { KVNamespaceInfo } from "../kv/helpers";
import type {
PostQueueBody,
Expand Down Expand Up @@ -10468,17 +10469,36 @@ export default{
});

describe("--x-provision", () => {
it("should accept KV, R2 and D1 bindings without IDs in the configuration file", async () => {
it("should inherit KV, R2 and D1 bindings if they could be found from the settings", async () => {
writeWorkerSource();
writeWranglerConfig({
main: "index.js",
kv_namespaces: [{ binding: "KV_NAMESPACE" }],
r2_buckets: [{ binding: "R2_BUCKET" }],
d1_databases: [{ binding: "D1_DATABASE" }],
});
mockGetSettings({
result: {
bindings: [
{
type: "kv_namespace",
name: "KV_NAMESPACE",
namespace_id: "kv-id",
},
{
type: "r2_bucket",
name: "R2_BUCKET",
bucket_name: "test-bucket",
},
{
type: "d1",
name: "D1_DATABASE",
id: "d1-id",
},
],
},
});
mockUploadWorkerRequest({
// We are treating them as inherited bindings temporarily to test the current implementation only
// This will be updated as we implement the actual provision logic
expectedBindings: [
{
name: "KV_NAMESPACE",
Expand Down Expand Up @@ -10517,6 +10537,65 @@ export default{
expect(std.err).toMatchInlineSnapshot(`""`);
expect(std.warn).toMatchInlineSnapshot(`""`);
});

it("should provision KV, R2 and D1 bindings if they couldn't be found from the settings", async () => {
writeWorkerSource();
writeWranglerConfig({
main: "index.js",
kv_namespaces: [{ binding: "KV_NAMESPACE" }],
// r2_buckets: [{ binding: "R2_BUCKET" }],
// d1_databases: [{ binding: "D1_DATABASE" }],
});
mockGetSettings();
mockCreateKvNamespace({
resultId: "kv-id",
});
mockConfirm({
text: "Would you like Wrangler to provision these resources on your behalf and bind them to your project?",
result: true,
});
mockUploadWorkerRequest({
expectedBindings: [
{
name: "KV_NAMESPACE",
type: "kv_namespace",
namespace_id: "kv-id",
},
// {
// name: "R2_BUCKET",
// type: "inherit",
// },
// {
// name: "D1_DATABASE",
// type: "inherit",
// },
],
});
mockSubDomainRequest();

await expect(
runWrangler("deploy --x-provision")
).resolves.toBeUndefined();
expect(std.out).toMatchInlineSnapshot(`
"Total Upload: xx KiB / gzip: xx KiB
Your worker has access to the following bindings:
- KV Namespaces:
- KV_NAMESPACE: (remote)
Provisioning resources...
All resources provisioned, continuing deployment...
Worker Startup Time: 100 ms
Your worker has access to the following bindings:
- KV Namespaces:
- KV_NAMESPACE: kv-id
Uploaded test-name (TIMINGS)
Deployed test-name triggers (TIMINGS)
https://test-name.test-sub-domain.workers.dev
Current Version ID: Galaxy-Class"
`);
expect(std.err).toMatchInlineSnapshot(`""`);
expect(std.warn).toMatchInlineSnapshot(`""`);
});
});

describe("queues", () => {
Expand Down Expand Up @@ -12325,6 +12404,72 @@ function mockServiceScriptData(options: {
}
}

function mockGetSettings(
options: {
result?: Settings;
assertAccountId?: string;
assertScriptName?: string;
} = {}
) {
msw.use(
http.get(
"*/accounts/:accountId/workers/scripts/:scriptName/settings",
async ({ params }) => {
if (options.assertAccountId) {
expect(params.accountId).toEqual(options.assertAccountId);
}

if (options.assertScriptName) {
expect(params.scriptName).toEqual(options.assertScriptName);
}

if (!options.result) {
return new Response(null, { status: 404 });
}

return HttpResponse.json({
success: true,
errors: [],
messages: [],
result: options.result,
});
}
)
);
}

function mockCreateKvNamespace(
options: {
resultId?: string;
assertAccountId?: string;
assertTitle?: string;
} = {}
) {
msw.use(
http.post(
"*/accounts/:accountId/storage/kv/namespaces",
async ({ request, params }) => {
if (options.assertAccountId) {
expect(params.accountId).toEqual(options.assertAccountId);
}

if (options.assertTitle) {
const requestBody = await request.json();
const title =
typeof requestBody === "object" ? requestBody?.title : null;
expect(title).toEqual(options.assertTitle);
}

return HttpResponse.json(
createFetchResult({ id: options.resultId ?? "some-namespace-id" }),
{ status: 200 }
);
},
{ once: true }
)
);
}

function mockGetQueueByName(queueName: string, queue: QueueResponse | null) {
const requests = { count: 0 };
msw.use(
Expand Down
47 changes: 29 additions & 18 deletions packages/wrangler/src/d1/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,34 @@ import type {
} from "../yargs-types";
import type { DatabaseCreationResult } from "./types";

export async function createD1Database(
accountId: string,
name: string,
location?: string
) {
try {
return await fetchResult<DatabaseCreationResult>(
`/accounts/${accountId}/d1/database`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,
...(location && { primary_location_hint: location }),
}),
}
);
} catch (e) {
if ((e as { code: number }).code === 7502) {
throw new UserError("A database with that name already exists");
}

throw e;
}
}

export function Options(yargs: CommonYargsArgv) {
return yargs
.positional("name", {
Expand Down Expand Up @@ -42,24 +70,7 @@ export const Handler = withConfig<HandlerOptions>(
}
}

let db: DatabaseCreationResult;
try {
db = await fetchResult(`/accounts/${accountId}/d1/database`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name,
...(location && { primary_location_hint: location }),
}),
});
} catch (e) {
if ((e as { code: number }).code === 7502) {
throw new UserError("A database with that name already exists");
}
throw e;
}
const db = await createD1Database(accountId, name, location);

logger.log(
`✅ Successfully created DB '${db.name}'${
Expand Down
3 changes: 2 additions & 1 deletion packages/wrangler/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { cancel } from "@cloudflare/cli";
import { syncAssets } from "../assets";
import { fetchListResult, fetchResult } from "../cfetch";
import { configFileName, formatConfigSnippet, printBindings } from "../config";
import { getBindings } from "../deployment-bundle/bindings";
import { getBindings, provisionBindings } from "../deployment-bundle/bindings";
import { bundleWorker } from "../deployment-bundle/bundle";
import {
printBundleSize,
Expand Down Expand Up @@ -788,6 +788,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
} else {
assert(accountId, "Missing accountId");

await provisionBindings(bindings, accountId, scriptName);
await ensureQueuesExistByConfig(config);
let bindingsPrinted = false;

Expand Down
Loading

0 comments on commit 04b5b5b

Please sign in to comment.