Add support for binary payloads#55
Conversation
Captures the declared media type on body parameters so converters can record it at parse time and the generator can route binary/text payloads without sniffing schemas. The field is allocator-owned and freed in Parameter.deinit. No behavior change yet — converters and generator are wired in subsequent commits.
Records the selected media type on Parameter.content_type with the existing JSON-first preference rule (application/json wins; then any *+json suffix; then the first declared media type). Strings are duped into the unified allocator. ref request bodies stay null pending a future ref-resolution follow-up. No generator behavior change yet.
Mirrors the v3.0 selection rule on Parameter.content_type for the v3.1 and v3.2 converters: application/json wins, then any *+json suffix, then the first declared media type. Strings are duped into the unified allocator. Adds smoke tests covering octet-stream and JSON-vs-XML.
Threads operation-level consumes (with spec-level fallback) into convertParameter for body parameters. Applies the same JSON-preference rule used by the v3.x converters: application/json wins, then any *+json suffix, otherwise the first listed media type. Adds a minimal Swagger 2.0 fixture exercising both operation-level octet-stream and spec-level JSON inheritance, and pairs it with two converter tests.
Adds a private BodyKind enum (none/json/binary/text/form) and a classifyBody helper that maps content-type strings to body kinds. Pure refactor: no call sites yet, generated output is byte-identical. Unit-tests cover JSON, +json suffix, octet-stream, image/audio/video, */*, text/*, multipart/*, and x-www-form-urlencoded.
Switches the runtime appendClientHeaders helper from a bool flag plus hard-coded application/json to an optional content_type parameter. Updates the three emit sites (requestRaw, SSE, direct path) to pass "application/json" or null, preserving runtime behaviour for JSON operations. Generated client snapshots refreshed accordingly.
Resolves #53. Operations whose request body content type classifies as binary (octet-stream, image/*, audio/*, video/*, */*, application/*) or text (text/*) now generate `requestBody: []const u8` parameters and pass the bytes straight to `http.fetch` with the captured Content-Type header. Multipart/x-www-form-urlencoded bodies still fall back to JSON encoding with a TODO marker pending follow-up work. JSON-bodied operations are unchanged. Generated client snapshots refreshed; uploadFile in generated_v3.zig now takes `[]const u8`, emits `application/octet-stream`, and never JSON-stringifies the payload.
Adds a short Request Body Content Types subsection describing the new []const u8 parameter for binary/text bodies and the JSON-fallback behaviour for multipart/form-data and x-www-form-urlencoded request bodies pending follow-up work.
Scribe closeout for binary request-body support (issue #53): - Merged design, test plan, and approval artifacts into decisions.md - Recorded orchestration logs for Lando (design + review), Fenster (implementation), Starkiller (test planning) - Updated agent history files with outcome summary - All validation gates green: zig build test, zig build run-generate - Binary payloads now flow as []const u8 with correct Content-Type headers - JSON bodies unchanged (snapshot stability preserved)
The 'loadFromUrl returns ConnectionFailed for unreachable host' test made a real TCP connection to 192.0.2.1:9999 on every 'zig build test' run. This is non-deterministic: the connect either crashes a worker thread with error.Unexpected (IO_TIMEOUT) during shutdown on Windows, or returns a LoadError other than ConnectionFailed, failing the assertion. Gate it behind skipIntegrationTests() like the other network tests, matching the file's documented intent that network tests are skipped by default to keep unit tests deterministic.
Under zig build test the test executable runs with --listen=- (IPC over stdout via std.zig.Server). On Zig 0.16/Windows, any byte written to stderr from within a test (std.debug.print, or std.log.warn/err which route through it) makes the test process exit nonzero, which the build reports as ailed command: ...test.exe ... --listen=-. std.log.info/debug are filtered at the default testing.log_level (.warn) and write nothing, so they are safe. Changes: - Test files: replace informational std.debug.print with std.log.info (silent at the default test log level). - Leak-detection blocks: replace the print-on-leak with @Panic so a leak now fails the test instead of merely printing (and breaking the runner). - generator.zig: remove redundant error-context prints (errors are already surfaced by main.zig) and convert progress messages to std.log.info. - input_loader.zig: convert error-context prints to std.log.info, preserving file/URL context for the CLI while keeping tests silent. Verified: clean-cache zig build test passes with no ailed command; zig build run-generate-v3 and running the generated code still succeed; zig fmt --check passes on LF.
|
Linter diff in the way? Review this PR in Change Stack to focus on meaningful changes and expand context only when needed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (7)
📒 Files selected for processing (24)
📝 WalkthroughWalkthroughThis PR implements media-type-aware request-body support for binary content. The changes introduce media-type parsing utilities, extend the parameter model to capture content types, update all OpenAPI/Swagger converters to select and preserve media types using priority rules, classify request bodies in the code generator to emit appropriate payloads ( ChangesBinary Request-Body Implementation
Test Coverage and Logging Infrastructure
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/tests/binary_payload_tests.zig`:
- Around line 83-309: Several tests (e.g., test "v3.0 converter :: octet-stream
uploadFile body captures content_type", "v3.0 converter :: addPet body prefers
application/json content_type", "v3.0 converter :: JSON wins when both JSON and
XML present", and the other eight listed) create a test allocator via
test_utils.createTestAllocator() but never call the required leak-checking
defer; after each occurrence of "const allocator = gpa.allocator();" add "defer
std.debug.assert(gpa.deinit() == .ok);" so each test that declares var gpa =
test_utils.createTestAllocator() (look for the gpa/allocator pattern inside the
tests named in the diff and functions OpenApiConverter.init /
OpenApi31Converter.init / OpenApi32Converter.init / SwaggerConverter.init usage)
performs the proper deinit assertion to enable leak detection.
- Around line 311-337: Both tests in this file use testing.allocator directly
(see the two test blocks "generated v3.0 :: uploadFile takes []const u8
requestBody and emits octet-stream Content-Type" and "generated v3.0 :: addPet
still uses JSON encoding for application/json body"); replace those usages with
a test allocator created via test_utils.createTestAllocator() (e.g., const
allocator = test_utils.createTestAllocator();), use that allocator for
readFileAlloc and any allocations, and ensure you call the allocator
cleanup/destructor (defer allocator.destroy() or the project’s recommended
teardown) to enable leak detection; update references to allocator in both test
blocks (file_contents, defer allocator.free(file_contents), etc.) accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b6a32088-517f-4177-9177-7fa607e7a0eb
⛔ Files ignored due to path filters (7)
generated/generated_v2.zigis excluded by!**/generated/**generated/generated_v2_yaml.zigis excluded by!**/generated/**generated/generated_v3.zigis excluded by!**/generated/**generated/generated_v31.zigis excluded by!**/generated/**generated/generated_v31_yaml.zigis excluded by!**/generated/**generated/generated_v32.zigis excluded by!**/generated/**generated/generated_v3_yaml.zigis excluded by!**/generated/**
📒 Files selected for processing (24)
.squad/agents/fenster/history.md.squad/agents/lando/history.md.squad/agents/starkiller/history.md.squad/decisions.mdREADME.mdopenapi/v2.0/binary-upload.jsonsrc/generator.zigsrc/generators/converters/openapi31_converter.zigsrc/generators/converters/openapi32_converter.zigsrc/generators/converters/openapi_converter.zigsrc/generators/converters/swagger_converter.zigsrc/generators/unified/api_generator.zigsrc/generators/unified/model_generator.zigsrc/input_loader.zigsrc/models/common/document.zigsrc/tests.zigsrc/tests/binary_payload_tests.zigsrc/tests/comprehensive_converter_tests.zigsrc/tests/openapi_v31_tests.zigsrc/tests/openapi_v32_tests.zigsrc/tests/openapi_v3_tests.zigsrc/tests/swagger_v2_tests.zigsrc/tests/test_input_loader.zigsrc/tests/unified_converter_tests.zig
There was a problem hiding this comment.
Pull request overview
This PR adds first-class support for non-JSON request bodies by propagating request-body media type through the unified IR and using it to generate raw-byte request payload handling (fixing issue #53 where application/octet-stream bodies were always JSON-encoded).
Changes:
- Extend the unified
Parametermodel withcontent_typeand capture it in v2.0/v3.0/v3.1/v3.2 converters (JSON-first selection). - Update the unified API generator to classify body kind (json/binary/text/form) and emit
[]const u8+ raw payload handling and dynamicContent-Typeheader when appropriate. - Add targeted tests + fixtures, adjust tests/logging to avoid stderr output, and document binary body support/limits in the README.
Reviewed changes
Copilot reviewed 24 out of 31 changed files in this pull request and generated 18 comments.
Show a summary per file
| File | Description |
|---|---|
| src/models/common/document.zig | Adds Parameter.content_type with ownership/deinit support. |
| src/generators/converters/openapi_converter.zig | Captures selected request-body media type for OpenAPI v3.0. |
| src/generators/converters/openapi31_converter.zig | Captures selected request-body media type for OpenAPI v3.1. |
| src/generators/converters/openapi32_converter.zig | Captures selected request-body media type for OpenAPI v3.2. |
| src/generators/converters/swagger_converter.zig | Resolves Swagger v2 consumes into Parameter.content_type for body params. |
| src/generators/unified/api_generator.zig | Adds body-kind classification and generates raw-byte payload + dynamic Content-Type. |
| src/generators/unified/model_generator.zig | Adjusts reserved identifier list (notably removing type). |
| src/generator.zig | Switches status output to std.log.info and removes some debug prints. |
| src/input_loader.zig | Replaces debug prints with std.log.info in load errors. |
| README.md | Documents request body content-type behavior and current limitations. |
| openapi/v2.0/binary-upload.json | Adds a Swagger v2 fixture for binary upload/consumes precedence. |
| src/tests/binary_payload_tests.zig | New tests for IR capture + generated-code assertions for binary vs JSON bodies. |
| src/tests.zig | Registers the new binary payload test module. |
| src/tests/test_input_loader.zig | Avoids stderr prints, gates unreachable-host test behind integration flag. |
| src/tests/unified_converter_tests.zig | Switches test diagnostics from std.debug.print to std.log.info. |
| src/tests/openapi_v3_tests.zig | Switches test diagnostics from std.debug.print to std.log.info. |
| src/tests/openapi_v31_tests.zig | Switches test diagnostics from std.debug.print to std.log.info. |
| src/tests/openapi_v32_tests.zig | Switches test diagnostics from std.debug.print to std.log.info. |
| src/tests/swagger_v2_tests.zig | Switches test diagnostics from std.debug.print to std.log.info. |
| src/tests/comprehensive_converter_tests.zig | Switches test diagnostics from std.debug.print to std.log.info. |
| generated/generated_v2.zig | Regenerated snapshot reflecting header handling changes. |
| generated/generated_v2_yaml.zig | Regenerated snapshot reflecting header handling changes. |
| generated/generated_v3.zig | Regenerated snapshot reflecting binary upload raw-body path. |
| generated/generated_v3_yaml.zig | Regenerated snapshot reflecting binary upload raw-body path. |
| generated/generated_v31.zig | Regenerated snapshot reflecting header helper signature changes. |
| generated/generated_v31_yaml.zig | Regenerated snapshot reflecting header helper signature changes. |
| generated/generated_v32.zig | Regenerated snapshot reflecting header helper signature changes. |
| .squad/decisions.md | Records binary request body support decision/log entry. |
| .squad/agents/starkiller/history.md | Adds Starkiller binary payload test-plan history note. |
| .squad/agents/lando/history.md | Adds Lando design/review notes for binary payload work. |
| .squad/agents/fenster/history.md | Adds Fenster implementation history note for binary payload support. |
| fn classifyBody(content_type: ?[]const u8) BodyKind { | ||
| const ct = content_type orelse return .json; | ||
| if (ct.len == 0) return .json; | ||
| if (std.ascii.eqlIgnoreCase(ct, "application/json")) return .json; | ||
| if (endsWithIgnoreCase(ct, "+json")) return .json; | ||
| if (std.ascii.eqlIgnoreCase(ct, "application/x-www-form-urlencoded")) return .form; | ||
| if (startsWithIgnoreCase(ct, "multipart/")) return .form; | ||
| if (startsWithIgnoreCase(ct, "text/")) return .text; | ||
| if (std.ascii.eqlIgnoreCase(ct, "application/octet-stream")) return .binary; | ||
| if (startsWithIgnoreCase(ct, "image/")) return .binary; | ||
| if (startsWithIgnoreCase(ct, "audio/")) return .binary; | ||
| if (startsWithIgnoreCase(ct, "video/")) return .binary; | ||
| if (std.ascii.eqlIgnoreCase(ct, "*/*")) return .binary; | ||
| if (startsWithIgnoreCase(ct, "application/")) return .binary; | ||
| return .binary; | ||
| } |
| @@ -1347,7 +1455,13 @@ pub const UnifiedApiGenerator = struct { | |||
| try self.buffer.appendSlice(self.allocator, " var headers = std.ArrayList(std.http.Header).empty;\n"); | |||
| try self.buffer.appendSlice(self.allocator, " defer headers.deinit(allocator);\n"); | |||
| try self.buffer.appendSlice(self.allocator, " const auth_header = try appendClientHeaders(allocator, &headers, client, "); | |||
| try self.buffer.appendSlice(self.allocator, if (has_body_param) "true" else "false"); | |||
| if (has_body_param) { | |||
| try self.buffer.appendSlice(self.allocator, "\""); | |||
| try self.buffer.appendSlice(self.allocator, direct_ct); | |||
| try self.buffer.appendSlice(self.allocator, "\""); | |||
| var mut_request_body = requestBody.*; | ||
| var schema: ?Schema = null; | ||
| var selected_key: ?[]const u8 = null; | ||
| if (mut_request_body.content.get("application/json")) |media_type| { | ||
| selected_key = "application/json"; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } else if (selectJsonSuffixKey(mut_request_body.content)) |key| { | ||
| if (mut_request_body.content.get(key)) |media_type| { | ||
| selected_key = key; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } else if (mut_request_body.content.count() > 0) { | ||
| var it = mut_request_body.content.iterator(); | ||
| if (it.next()) |entry| { | ||
| selected_key = entry.key_ptr.*; | ||
| if (entry.value_ptr.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } |
| var mut_request_body = requestBody.*; | ||
| var schema: ?Schema = null; | ||
| var selected_key: ?[]const u8 = null; | ||
| if (mut_request_body.content.get("application/json")) |media_type| { | ||
| selected_key = "application/json"; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } else if (selectJsonSuffixKey31(mut_request_body.content)) |key| { | ||
| if (mut_request_body.content.get(key)) |media_type| { | ||
| selected_key = key; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } else if (mut_request_body.content.count() > 0) { | ||
| var it = mut_request_body.content.iterator(); | ||
| if (it.next()) |entry| { | ||
| selected_key = entry.key_ptr.*; | ||
| if (entry.value_ptr.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } |
| var mut_request_body = requestBody.*; | ||
| var schema: ?Schema = null; | ||
| var selected_key: ?[]const u8 = null; | ||
| if (mut_request_body.content.get("application/json")) |media_type| { | ||
| selected_key = "application/json"; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } else if (selectJsonSuffixKey32(mut_request_body.content)) |key| { | ||
| if (mut_request_body.content.get(key)) |media_type| { | ||
| selected_key = key; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } else if (mut_request_body.content.count() > 0) { | ||
| var it = mut_request_body.content.iterator(); | ||
| if (it.next()) |entry| { | ||
| selected_key = entry.key_ptr.*; | ||
| if (entry.value_ptr.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } |
| @@ -1347,7 +1455,13 @@ pub const UnifiedApiGenerator = struct { | |||
| try self.buffer.appendSlice(self.allocator, " var headers = std.ArrayList(std.http.Header).empty;\n"); | |||
| try self.buffer.appendSlice(self.allocator, " defer headers.deinit(allocator);\n"); | |||
| try self.buffer.appendSlice(self.allocator, " const auth_header = try appendClientHeaders(allocator, &headers, client, "); | |||
| try self.buffer.appendSlice(self.allocator, if (has_body_param) "true" else "false"); | |||
| if (has_body_param) { | |||
| try self.buffer.appendSlice(self.allocator, "\""); | |||
| try self.buffer.appendSlice(self.allocator, direct_ct); | |||
| try self.buffer.appendSlice(self.allocator, "\""); | |||
| var mut_request_body = requestBody.*; | ||
| var schema: ?Schema = null; | ||
| var selected_key: ?[]const u8 = null; | ||
| if (mut_request_body.content.get("application/json")) |media_type| { | ||
| selected_key = "application/json"; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } else if (selectJsonSuffixKey(mut_request_body.content)) |key| { | ||
| if (mut_request_body.content.get(key)) |media_type| { | ||
| selected_key = key; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } else if (mut_request_body.content.count() > 0) { | ||
| var it = mut_request_body.content.iterator(); | ||
| if (it.next()) |entry| { | ||
| selected_key = entry.key_ptr.*; | ||
| if (entry.value_ptr.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } |
| var mut_request_body = requestBody.*; | ||
| var schema: ?Schema = null; | ||
| var selected_key: ?[]const u8 = null; | ||
| if (mut_request_body.content.get("application/json")) |media_type| { | ||
| selected_key = "application/json"; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } else if (selectJsonSuffixKey31(mut_request_body.content)) |key| { | ||
| if (mut_request_body.content.get(key)) |media_type| { | ||
| selected_key = key; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } else if (mut_request_body.content.count() > 0) { | ||
| var it = mut_request_body.content.iterator(); | ||
| if (it.next()) |entry| { | ||
| selected_key = entry.key_ptr.*; | ||
| if (entry.value_ptr.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } |
| var mut_request_body = requestBody.*; | ||
| var schema: ?Schema = null; | ||
| var selected_key: ?[]const u8 = null; | ||
| if (mut_request_body.content.get("application/json")) |media_type| { | ||
| selected_key = "application/json"; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } else if (selectJsonSuffixKey32(mut_request_body.content)) |key| { | ||
| if (mut_request_body.content.get(key)) |media_type| { | ||
| selected_key = key; | ||
| if (media_type.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } else if (mut_request_body.content.count() > 0) { | ||
| var it = mut_request_body.content.iterator(); | ||
| if (it.next()) |entry| { | ||
| selected_key = entry.key_ptr.*; | ||
| if (entry.value_ptr.schema) |schema_or_ref| { | ||
| schema = try self.convertSchemaOrReference(schema_or_ref); | ||
| } | ||
| } | ||
| } |
| fn selectConsumesMedia(list: []const []const u8) ?[]const u8 { | ||
| if (list.len == 0) return null; | ||
| for (list) |m| { | ||
| if (std.mem.eql(u8, m, "application/json")) return m; | ||
| } | ||
| for (list) |m| { | ||
| if (std.mem.endsWith(u8, m, "+json")) return m; | ||
| } | ||
| return list[0]; | ||
| } |
Address PR #55 review (copilot-pull-request-reviewer): media types with parameters (e.g. "application/json; charset=utf-8") were compared verbatim, causing misclassification and non-deterministic JSON-first selection. - Add src/media_type.zig with baseMediaType/isJson/isJsonSuffix helpers and a deterministic selectBestJsonKey (exact JSON > +json suffix > lexicographic fallback), with unit tests. - classifyBody: strip parameters before classification so parameterized JSON bodies are JSON-encoded rather than emitted as raw []const u8. - generateFunctionBodyDirect: force Content-Type application/json for form bodies that fall back to JSON encoding (multipart/x-www-form-urlencoded not yet supported) so the header matches the actual payload. - OpenAPI v3.0/v3.1/v3.2 convertRequestBody: select best content-type key via normalized comparison; remove now-unused selectJsonSuffixKey helpers. - Swagger v2 selectConsumesMedia: compare on normalized base media type while returning the original string for header emission. - Add regression tests for parameterized media types. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| fn isReservedIdent(name: []const u8) bool { | ||
| const reserved = [_][]const u8{ | ||
| "addrspace", "align", "allowzero", "and", "anyerror", "anyframe", "anyopaque", "anytype", | ||
| "asm", "async", "await", "bool", "break", "callconv", "catch", "comptime", | ||
| "const", "continue", "defer", "else", "enum", "errdefer", "error", "export", | ||
| "extern", "false", "fn", "for", "if", "inline", "isize", "linksection", | ||
| "noalias", "noreturn", "nosuspend", "null", "opaque", "or", "orelse", "packed", | ||
| "pub", "resume", "return", "struct", "suspend", "switch", "test", "threadlocal", | ||
| "true", "try", "type", "undefined", "union", "unreachable", "usize", "usingnamespace", | ||
| "var", "void", "volatile", "while", | ||
| "addrspace", "align", "allowzero", "and", "anyerror", "anyframe", "anyopaque", "anytype", | ||
| "asm", "async", "await", "bool", "break", "callconv", "catch", "comptime", | ||
| "const", "continue", "defer", "else", "enum", "errdefer", "error", "export", | ||
| "extern", "false", "fn", "for", "if", "inline", "isize", "linksection", | ||
| "noalias", "noreturn", "nosuspend", "null", "opaque", "or", "orelse", "packed", | ||
| "pub", "resume", "return", "struct", "suspend", "switch", "test", "threadlocal", | ||
| "true", "try", "undefined", "union", "unreachable", "usize", "usingnamespace", "var", | ||
| "void", "volatile", "while", | ||
| }; |
| \\pub fn requestRaw(client: *Client, method: std.http.Method, url: []const u8, payload: ?[]const u8) !RawResponse { | ||
| \\ const allocator = client.allocator; | ||
| \\ var headers = std.ArrayList(std.http.Header).empty; | ||
| \\ defer headers.deinit(allocator); | ||
| \\ const auth_header = try appendClientHeaders(allocator, &headers, client, payload != null, "application/json"); | ||
| \\ const content_type: ?[]const u8 = if (payload != null) "application/json" else null; | ||
| \\ const auth_header = try appendClientHeaders(allocator, &headers, client, content_type, "application/json"); | ||
| \\ defer if (auth_header) |value| allocator.free(value); |
…tRaw Address PR #55 review (copilot-pull-request-reviewer, 2 findings): - model_generator: re-add "type" to the reserved-identifier list. Although a bare 'type' is valid as a struct *field* name, the same appendIdentifier path also emits declaration/type names, where 'pub const type = struct {...}' is invalid Zig ("name shadows primitive 'type'"). Escaping as @"type" keeps all contexts valid; regenerated snapshots accordingly. - api_generator: requestRaw hard-coded Content-Type application/json for any payload. Add requestRawWithContentType helper (requestRaw delegates with application/json) and route declared non-application/json JSON media types (vendor +json, parameterized keys) through it from generateFunctionRaw so the emitted Content-Type matches Parameter.content_type. - Add a regression test asserting a vendor +json body propagates its media type to requestRawWithContentType. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Closes #53
Summary by CodeRabbit
Release Notes
New Features
Content-Typeheader generation.Documentation
Tests