History

fix(openapi): accept a string body for octet-stream uploads

A plain string `body` was being rejected for octet-stream operations with "request body must be bytes; provide bodyBase64", which broke existing callers (such as the Microsoft Graph drive item content upload) that send text content as a string. The request layer already handles a string body, so let it through as UTF-8 bytes. Binary uploads still use bodyBase64.

Rhys Sullivan committed 4 months ago

Commit 612f03e1 parent 529b9083 files · +25 −10Browse files

Unified · Split

3 files
  1. .changeset/octet-stream-string-body.md +5 −0
  2. packages/plugins/openapi/src/sdk/invoke.ts +10 −1
  3. packages/plugins/openapi/src/sdk/non-json-body.test.ts +10 −9
.changeset/octet-stream-string-body.md+5−0
@@ -0,0 +1,5 @@
1---
2"@executor-js/plugin-openapi": patch
3---
4
5Allow a plain string `body` for octet-stream uploads again. Operations like Microsoft Graph's drive item content upload were rejecting string bodies with "request body must be bytes; provide bodyBase64", even though the request layer already sends a string through fine. String bodies now go through as UTF-8 bytes; binary content still uses `bodyBase64`.
packages/plugins/openapi/src/sdk/invoke.ts+10−1
@@ -782,7 +782,16 @@ export const invoke = Effect.fn("OpenApi.invoke")(function* ( · 781 unmodified lines
782 binaryBody?.ok === true && !octetStreamContent && isOctetStream(rb.contentType)782 binaryBody?.ok === true && !octetStreamContent && isOctetStream(rb.contentType)
783 ? rb.contentType783 ? rb.contentType
784 : (selected?.contentType ?? rb.contentType);784 : (selected?.contentType ?? rb.contentType);
785 if (isOctetStream(chosenCt) && toUint8Array(bodyValue) === null) {785 // A `bodyBase64` arg already decoded to bytes above. A plain string
786 // `body` is the long-standing way callers pass (text) octet-stream
787 // upload content, and applyRequestBody sends it through as-is, so let it
788 // past. Only a genuinely non-byte, non-string shape (an object, say)
789 // can't be uploaded as octet-stream and needs `bodyBase64`.
790 if (
791 isOctetStream(chosenCt) &&
792 typeof bodyValue !== "string" &&
793 toUint8Array(bodyValue) === null
794 ) {
786 return yield* new OpenApiInvocationError({795 return yield* new OpenApiInvocationError({
787 message: "application/octet-stream request body must be bytes; provide `bodyBase64`",796 message: "application/octet-stream request body must be bytes; provide `bodyBase64`",
788 statusCode: Option.none(),797 statusCode: Option.none(),
packages/plugins/openapi/src/sdk/non-json-body.test.ts+10−9
@@ -368,7 +368,7 @@ describe("OpenAPI non-JSON request body dispatch", () => { · 367 unmodified lines
368 }),368 }),
369 );369 );
370 370
371 it.effect("application/octet-stream: string body fails before dispatch", () =>371 it.effect("application/octet-stream: string body passes through as UTF-8 bytes", () =>
372 Effect.gen(function* () {372 Effect.gen(function* () {
373 const { server, captured } = yield* startEchoServer({373 const { server, captured } = yield* startEchoServer({
374 payload: Schema.Uint8Array.pipe(HttpApiSchema.asUint8Array()),374 payload: Schema.Uint8Array.pipe(HttpApiSchema.asUint8Array()),
@@ -380,15 +380,16 @@ describe("OpenAPI non-JSON request body dispatch", () => { · 5 unmodified lines
380 slug: "bin_string_body",380 slug: "bin_string_body",
381 });381 });
382 382
383 const exit = yield* executor383 // A plain string `body` is the long-standing way callers upload text
384 .execute(conn.address("body.submit"), {384 // content to an octet-stream endpoint, so it must reach the wire rather
385 body: "3q2+7w==",385 // than failing. It is sent verbatim as UTF-8 bytes (not base64-decoded),
386 })386 // which preserves the pre-bodyBase64 behavior; binary uploads still use
387 .pipe(Effect.exit);387 // `bodyBase64`.
388 const text = "plain file contents, not base64";
389 yield* executor.execute(conn.address("body.submit"), { body: text });
388 390
389 expect(Exit.isFailure(exit)).toBe(true);391 expect(captured.contentType).toBe("application/octet-stream");
390 expect(captured.contentType).toBe("");392 expect(captured.body.toString("utf8")).toBe(text);
391 expect(captured.body.length).toBe(0);
392 }),393 }),
393 );394 );
394 395

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t