Skip to content

feat: multipart/form-data and form-urlencoded content type generation#23

Merged
halotukozak merged 32 commits intomasterfrom
feat/content-types
Mar 31, 2026
Merged

feat: multipart/form-data and form-urlencoded content type generation#23
halotukozak merged 32 commits intomasterfrom
feat/content-types

Conversation

@halotukozak
Copy link
Copy Markdown
Member

Summary

  • Extend SpecParser to resolve content types with priority: multipart > form-urlencoded > JSON
  • Add 10 Ktor form/multipart constants to Names.kt
  • Implement buildMultipartBody in ClientGenerator: submitFormWithBinaryData + formData {} builder, ChannelProvider for file params, ContentDisposition headers
  • Implement buildFormUrlEncodedBody: submitForm + parameters {} builder, individual typed params with toString() conversion
  • Verify CONT-03: 201 Created returns typed body, 204 No Content returns Unit

Test plan

  • Parser content type resolution tests
  • CONT-03 verification: 201 typed response + 204/200 priority
  • Multipart: single file, mixed text+file, multiple files, ChannelProvider params
  • Form-urlencoded: basic form, typed params, optional fields, non-POST method override
  • Manual: test against real specs with multipart/form endpoints

🤖 Generated with Claude Code

halotukozak and others added 5 commits March 23, 2026 10:19
…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>
Copilot AI review requested due to automatic review settings March 23, 2026 09:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 SpecParser requestBody content-type resolution with priority multipart > form-urlencoded > JSON.
  • Add Ktor forms/multipart KotlinPoet name constants in Names.kt.
  • Extend ClientGenerator to generate submitFormWithBinaryData / submitForm request 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.

Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 24, 2026

Coverage Report

Overall Project 94.4% -1.29% 🍏
Files changed 94.55% 🍏

File Coverage
Names.kt 100% 🍏
Packages.kt 100% 🍏
ParametersGenerator.kt 100% 🍏
ApiSpec.kt 100% 🍏
SerializersModuleGenerator.kt 100% 🍏
ApiClientBaseGenerator.kt 100% 🍏
Utils.kt 96.55% -3.45% 🍏
ClientGenerator.kt 96.5% -3.5% 🍏
BodyGenerator.kt 95.79% -4.21% 🍏
ModelGenerator.kt 95.59% 🍏
SpecParser.kt 93.89% -0.39% 🍏
ApiResponseGenerator.kt 92.42% 🍏
Utils.kt 82.44% -17.56% 🍏
CodeGenerator.kt 0% -26.14%

- 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.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/Names.kt Outdated
@halotukozak halotukozak requested a review from MattK97 March 25, 2026 15:10
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/Names.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/model/TypeRef.kt Outdated
…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.
…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.
@halotukozak halotukozak requested a review from MattK97 March 27, 2026 13:04
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/model/ApiSpec.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/client/ClientGenerator.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/Packages.kt Outdated
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/client/BodyGenerator.kt Outdated
ParameterSpec(name, prop.type.toTypeName()),
)
}
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to check here if isrequired?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@halotukozak halotukozak requested a review from MattK97 March 30, 2026 10:20
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/model/ApiSpec.kt
Copy link
Copy Markdown
Collaborator

@MattK97 MattK97 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼 one minor comment

@halotukozak halotukozak merged commit a6850cb into master Mar 31, 2026
1 check passed
@halotukozak halotukozak deleted the feat/content-types branch March 31, 2026 10:31
@halotukozak halotukozak added this to the 0.0.2 milestone Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants