feat: multipart/form-data and form-urlencoded content type generation#23
feat: multipart/form-data and form-urlencoded content type generation#23halotukozak merged 32 commits intomasterfrom
Conversation
…multipart constants - SpecParser resolves requestBody content type with priority: multipart > form-urlencoded > json - Add MULTIPART_FORM_DATA and FORM_URL_ENCODED constants to SpecParser - Add Ktor Forms & Multipart MemberName/ClassName constants to Names.kt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Test 201 Created with schema returns typed response (not Unit) - Test mixed 200/204 responses uses 200 schema type Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- multipart endpoint generates submitFormWithBinaryData call - ChannelProvider param for binary fields - text fields use simple append - binary fields include ContentDisposition header - existing JSON requestBody still generates setBody pattern Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Branch buildFunctionBody on content type (multipart/form/json) - buildMultipartBody generates submitFormWithBinaryData with formData builder - Binary fields generate ChannelProvider + fileName + contentType params - Text fields use simple append(key, value) pattern - ContentDisposition header with filename for binary parts - Extract buildUrlString, addHeaderParams, addQueryParams helpers - Add HEADERS_CLASS constant to Names.kt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- form-urlencoded endpoint generates submitForm with parameters builder - non-string params use toString() conversion - string params do NOT use toString() - optional fields generate nullable params with null guard Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds multipart/form-data and application/x-www-form-urlencoded support to the OpenAPI → Ktor client generation pipeline, spanning parsing (content-type selection), name constants, client codegen, and tests.
Changes:
- Update
SpecParserrequestBody content-type resolution with priority multipart > form-urlencoded > JSON. - Add Ktor forms/multipart KotlinPoet name constants in
Names.kt. - Extend
ClientGeneratorto generatesubmitFormWithBinaryData/submitFormrequest bodies and corresponding parameters; add tests for the new behaviors.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| core/src/test/kotlin/com/avsystem/justworks/core/gen/ClientGeneratorTest.kt | Adds tests for response code typing and basic multipart/form-urlencoded generation behavior. |
| core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecParser.kt | Chooses requestBody media type by priority and stores the chosen contentType on RequestBody. |
| core/src/main/kotlin/com/avsystem/justworks/core/gen/Names.kt | Introduces KotlinPoet MemberName/ClassName constants for Ktor forms/multipart APIs. |
| core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt | Implements multipart and form-urlencoded request generation and parameter shaping; refactors URL/header/query helpers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Coverage Report
|
- Simplify handling of request body parameters by consolidating logic. - Leverage schema's `properties` and `requiredProperties` directly, removing redundant helper methods. - Adjust related builder functions for improved clarity and conciseness.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…rameter naming in ClientGenerator
…ef` properties access - Consolidate logic for accessing `properties` and `requiredProperties`, leveraging null-safe operations with defaults. - Remove redundant `isBinaryUpload()` helper and relocate it to an appropriate context. - Simplify `TypeRef` interface by removing default implementations for `properties` and `requiredProperties`.
…nt` package - Improves structure by moving `ApiClientBaseGenerator` to a dedicated `client` package. - Adjust import paths accordingly for consistency and modularity.
- Move `ClientGenerator` to `client` package for improved structure and modularity. - Update imports and dependencies to align with the new structure.
…ametersGenerator` - Introduce `ParametersGenerator` to encapsulate parameter-building logic. - Remove and relocate redundant parameter generation methods from `BodyGenerator`. - Update references in `BodyGenerator` to use the new modularized `ParametersGenerator`.
…ean up redundant parameters - Change `TypeMapping` to `internal` for restricted visibility. - Add `context` receiver for `modelPackage` to simplify method calls. - Remove redundant `modelPackage` parameter in `toTypeName`.
…ed package handling - Introduce `Package` as a sealed interface with `ModelPackage` and `ApiPackage` implementations. - Add extension operators for `ClassName` and `MemberName` to streamline package-based instantiation.
…and clean up parameters - Change `SerializersModuleGenerator` to `internal` for improved encapsulation. - Remove redundant constructor and streamline `context` receiver usage. - Adjust `modelPackage` handling to use its `name` property directly.
…ontext` receiver - Add missing imports for `ClientGenerator` and `ApiClientBaseGenerator`. - Refactor `CodeGenerator` to use `context` receiver, streamlining the generation process. - Update method call to use `ClientGenerator.generate` directly.
…ccess and conditional code generation
…pe` enum - Remove hardcoded content type strings and replace with the new `ContentType` enum. - Update relevant logic in `ParametersGenerator`, `SpecParser`, and `BodyGenerator` to use `ContentType`. - Add helper operators to enhance `ContentType` usage with `Content`.
…text` usage, and remove redundant parameters - Refactor `ModelGenerator` to be an `internal object`, removing the need for a constructor. - Simplify methods by adding `context` receiver for `ModelPackage`. - Replace redundant parameterized calls with simplified `context` usage. - Introduce `String.toEnumOrNull` utility to replace enum `parse` methods and clean up related logic.
…ine generator calls - Update `clientClass` and related test methods to accept varargs instead of lists for endpoints. - Refactor generator method calls to use streamlined `generate` utility with `context` receiver. - Replace string-based content type assignments with `ContentType` constants.
- Extract `optionalGuard` utility from `Utils` and move it into `BodyGenerator`. - Simplify body construction in `BodyGenerator` using `CodeBlock.Builder` extensions. - Remove redundant `optionalGuard` and `addCommonRequestParts` logic calls. - Clean up imports and streamline method implementations for clarity.
- Remove `ApiClientBaseGenerator`, `ApiResponseGenerator`, and `ModelGenerator`, simplifying the codebase. - Eliminate related imports and redundant supporting logic.
…pping` - Move the `toTypeName` logic directly into `TypeRef` as an extension function with `context` support. - Delete the obsolete `TypeMapping` object and update all references across the codebase. - Simplify imports and ensure consistent usage of `TypeRef.toTypeName()`.
- Simplify usage of builder and helper methods across all generator files by removing explicit `Companion` references. - Update constructors, property builders, and annotations to utilize direct calls, improving readability and consistency.
- Remove unused imports in `Utils` and `TypeRef` files for improved clarity. - Inline multi-line `PropertySpec` builder calls in `ApiResponseGenerator` for consistency and readability.
# Conflicts: # core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecParser.kt
- Replace hardcoded `${BASE_URL}` syntax with `${$BASE_URL}` for correct variable interpolation.
- Add `BASE_URL` import to ensure proper resolution in URL generation.
| ParameterSpec(name, prop.type.toTypeName()), | ||
| ) | ||
| } | ||
| } |
There was a problem hiding this comment.
no need to check here if isrequired?
There was a problem hiding this comment.
Supporting nullable/optional multipart parameters requires design decisions beyond a simple isRequired check. For non-binary fields it's straightforward, but for binary uploads each field expands into 3 parameters (channelProvider, name, contentType) — making them all independently nullable would result in an awkward API. A cleaner approach would be to introduce a wrapper type (e.g. FileUpload?), but that's a broader change. Will address in a follow-up together with the optionalGuard for multipart body generation.
- Move `String.toEnumOrNull` utility to `core/utils` for better reuse and clean up related imports. - Make `Package` interface and constants in `ClientGenerator` internal for improved encapsulation. - Remove duplicate `isBinaryUpload` function and centralize its usage across generators. - Update `Endpoint` request body handling logic for stricter requirement checks.
Summary
SpecParserto resolve content types with priority: multipart > form-urlencoded > JSONNames.ktbuildMultipartBodyinClientGenerator:submitFormWithBinaryData+formData {}builder,ChannelProviderfor file params,ContentDispositionheadersbuildFormUrlEncodedBody:submitForm+parameters {}builder, individual typed params withtoString()conversionTest plan
🤖 Generated with Claude Code