Skip to content

Commit b7a9ce6

Browse files
GregBrimblesidharthachatterjeegeelen
authored
Use new bulk upload API for 'pages publish' (#1028)
* Use new bulk upload API for 'pages publish' * Only upload missing files * Get PAGES_API_HOST from Env * Using new /pages/assets endpoints * Added tests for pages bulk upload endpoints * fixing imports * removed authOverride by relaxing the constraint about overriding Authorization header * tweaked based on review comments * switched p-queue to be a devDep of Wrangler, since it'll be bundled Co-authored-by: Sid Chatterjee <[email protected]> Co-authored-by: Glen Maddern <[email protected]>
1 parent 175737f commit b7a9ce6

File tree

6 files changed

+303
-95
lines changed

6 files changed

+303
-95
lines changed

.changeset/unlucky-rocks-obey.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"wrangler": patch
3+
---
4+
5+
feat: Use new bulk upload API for 'wrangler pages publish'
6+
7+
This raises the file limit back up to 20k for a deployment.

package-lock.json

+75-15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/wrangler/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"jest-websocket-mock": "^2.3.0",
8888
"mime": "^3.0.0",
8989
"open": "^8.4.0",
90+
"p-queue": "^7.2.0",
9091
"pretty-bytes": "^6.0.0",
9192
"prompts": "^2.4.2",
9293
"react": "^17.0.2",
@@ -132,7 +133,7 @@
132133
"testTimeout": 30000,
133134
"testRegex": ".*.(test|spec)\\.[jt]sx?$",
134135
"transformIgnorePatterns": [
135-
"node_modules/(?!find-up|locate-path|p-locate|p-limit|yocto-queue|path-exists|execa|strip-final-newline|npm-run-path|path-key|onetime|mimic-fn|human-signals|is-stream|get-port|supports-color|pretty-bytes)"
136+
"node_modules/(?!find-up|locate-path|p-locate|p-limit|p-timeout|p-queue|yocto-queue|path-exists|execa|strip-final-newline|npm-run-path|path-key|onetime|mimic-fn|human-signals|is-stream|get-port|supports-color|pretty-bytes)"
136137
],
137138
"moduleNameMapper": {
138139
"clipboardy": "<rootDir>/src/__tests__/helpers/clipboardy-mock.js",

packages/wrangler/src/__tests__/pages.test.ts

+88-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { mockConsoleMethods } from "./helpers/mock-console";
55
import { runInTempDir } from "./helpers/run-in-tmp";
66
import { runWrangler } from "./helpers/run-wrangler";
77
import type { Project, Deployment } from "../pages";
8-
import type { File, FormData } from "undici";
8+
import type { FormData } from "undici";
99

1010
describe("pages", () => {
1111
runInTempDir();
@@ -279,21 +279,53 @@ describe("pages", () => {
279279
writeFileSync("logo.png", "foobar");
280280

281281
setMockResponse(
282-
"/accounts/:accountId/pages/projects/foo/file",
283-
async ([_url, accountId], init) => {
282+
"/accounts/:accountId/pages/projects/foo/upload-token",
283+
async ([_url, accountId]) => {
284284
expect(accountId).toEqual("some-account-id");
285-
expect(init.method).toEqual("POST");
286-
const body = init.body as FormData;
287-
const logoPNGFile = body.get("file") as File;
288-
expect(await logoPNGFile.text()).toEqual("foobar");
289-
expect(logoPNGFile.name).toEqual("logo.png");
290285

291286
return {
292-
id: "2082190357cfd3617ccfe04f340c6247",
287+
jwt: "<<funfetti-auth-jwt>>",
293288
};
294289
}
295290
);
296291

292+
setMockResponse(
293+
"/pages/assets/check-missing",
294+
"POST",
295+
async (_, init) => {
296+
expect(init.headers).toMatchObject({
297+
Authorization: "Bearer <<funfetti-auth-jwt>>",
298+
});
299+
const body = JSON.parse(init.body as string) as { hashes: string[] };
300+
expect(body).toMatchObject({
301+
hashes: ["2082190357cfd3617ccfe04f340c6247"],
302+
});
303+
return body.hashes;
304+
}
305+
);
306+
307+
setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
308+
expect(init.headers).toMatchObject({
309+
Authorization: "Bearer <<funfetti-auth-jwt>>",
310+
});
311+
const body = JSON.parse(init.body as string) as {
312+
key: string;
313+
value: string;
314+
metadata: { contentType: string };
315+
base64: boolean;
316+
}[];
317+
expect(body).toMatchObject([
318+
{
319+
key: "2082190357cfd3617ccfe04f340c6247",
320+
value: Buffer.from("foobar").toString("base64"),
321+
metadata: {
322+
contentType: "image/png",
323+
},
324+
base64: true,
325+
},
326+
]);
327+
});
328+
297329
setMockResponse(
298330
"/accounts/:accountId/pages/projects/foo/deployments",
299331
async ([_url, accountId], init) => {
@@ -326,15 +358,58 @@ describe("pages", () => {
326358

327359
it("should not error when directory names contain periods and houses a extensionless file", async () => {
328360
mkdirSync(".well-known");
361+
// Note: same content as previous test, but since it's a different extension,
362+
// it hashes to a different value
329363
writeFileSync(".well-known/foobar", "foobar");
330364

331365
setMockResponse(
332-
"/accounts/:accountId/pages/projects/foo/file",
333-
async () => ({
334-
id: "7b764dacfd211bebd8077828a7ddefd7",
335-
})
366+
"/accounts/:accountId/pages/projects/foo/upload-token",
367+
async ([_url, accountId]) => {
368+
expect(accountId).toEqual("some-account-id");
369+
370+
return {
371+
jwt: "<<funfetti-auth-jwt>>",
372+
};
373+
}
374+
);
375+
376+
setMockResponse(
377+
"/pages/assets/check-missing",
378+
"POST",
379+
async (_, init) => {
380+
expect(init.headers).toMatchObject({
381+
Authorization: "Bearer <<funfetti-auth-jwt>>",
382+
});
383+
const body = JSON.parse(init.body as string) as { hashes: string[] };
384+
expect(body).toMatchObject({
385+
hashes: ["7b764dacfd211bebd8077828a7ddefd7"],
386+
});
387+
return body.hashes;
388+
}
336389
);
337390

391+
setMockResponse("/pages/assets/upload", "POST", async (_, init) => {
392+
expect(init.headers).toMatchObject({
393+
Authorization: "Bearer <<funfetti-auth-jwt>>",
394+
});
395+
const body = JSON.parse(init.body as string) as {
396+
key: string;
397+
value: string;
398+
metadata: { contentType: string };
399+
base64: boolean;
400+
}[];
401+
expect(body).toMatchObject([
402+
{
403+
key: "7b764dacfd211bebd8077828a7ddefd7",
404+
value: Buffer.from("foobar").toString("base64"),
405+
metadata: {
406+
contentType: "application/octet-stream",
407+
},
408+
base64: true,
409+
},
410+
]);
411+
});
412+
338413
setMockResponse(
339414
"/accounts/:accountId/pages/projects/foo/deployments",
340415
async () => ({

packages/wrangler/src/cfetch/internal.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export async function fetchInternal<ResponseType>(
3232
await requireLoggedIn();
3333
const apiToken = requireApiToken();
3434
const headers = cloneHeaders(init.headers);
35-
addAuthorizationHeader(headers, apiToken);
35+
addAuthorizationHeaderIfUnspecified(headers, apiToken);
3636

3737
const queryString = queryParams ? `?${queryParams.toString()}` : "";
3838
const method = init.method ?? "GET";
@@ -96,16 +96,13 @@ function requireApiToken(): string {
9696
return authToken;
9797
}
9898

99-
function addAuthorizationHeader(
99+
function addAuthorizationHeaderIfUnspecified(
100100
headers: Record<string, string>,
101101
apiToken: string
102102
): void {
103-
if ("Authorization" in headers) {
104-
throw new Error(
105-
"The request already specifies an authorisation header - cannot add a new one."
106-
);
103+
if (!("Authorization" in headers)) {
104+
headers["Authorization"] = `Bearer ${apiToken}`;
107105
}
108-
headers["Authorization"] = `Bearer ${apiToken}`;
109106
}
110107

111108
/**

0 commit comments

Comments
 (0)