Skip to content

Code generators (model, client, response, serializers) - #4

Closed
halotukozak wants to merge 12 commits into
feat/model-parserfrom
feat/generators
Closed

Code generators (model, client, response, serializers)#4
halotukozak wants to merge 12 commits into
feat/model-parserfrom
feat/generators

Conversation

@halotukozak

Copy link
Copy Markdown
Contributor

Summary

  • ModelGenerator — data classes, sealed interfaces, enums, type aliases from OpenAPI schemas
  • ClientGenerator — per-tag Ktor HTTP client classes with suspend functions
  • ApiResponseGenerator — sealed ApiResponse hierarchy (HttpSuccess, HttpError)
  • SerializersModuleGenerator — kotlinx.serialization module for polymorphic types
  • TypeMapping, NameUtils, Names, InlineSchemaDeduplicator utilities
  • Full test coverage (8 test files)

Test plan

  • ./gradlew core:test passes

Depends on #2

🤖 Generated with Claude Code

halotukozak and others added 3 commits March 11, 2026 14:35
Gradle build configuration with CI
chore: add .editorconfig and integrate ktlint
ModelGenerator, ClientGenerator, ApiResponseGenerator, SerializersModuleGenerator,
TypeMapping, NameUtils, Names, InlineSchemaDeduplicator with full test coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 11, 2026 19:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces the core OpenAPI→Kotlin code generation pipeline: parsing + validation of specs into an intermediate model, then generating Kotlin model types, Ktor clients, response wrappers, and serializers wiring—backed by comprehensive tests and fixtures.

Changes:

  • Added SpecParser + SpecValidator and expanded test fixtures for refs, polymorphism, anyOf/oneOf, and Swagger v2 conversion.
  • Implemented generators for models (ModelGenerator), clients (ClientGenerator), API responses (ApiResponseGenerator), and polymorphic serializers module (SerializersModuleGenerator), plus supporting utilities.
  • Added extensive unit tests across parsing, naming, type mapping, and code generation behavior.

Reviewed changes

Copilot reviewed 33 out of 33 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
core/build.gradle.kts Adds Arrow dependency and enables -Xcontext-parameters; pins ktlint version.
core/src/main/kotlin/com/avsystem/justworks/core/Generator.kt Adds generator entry point placeholder.
core/src/main/kotlin/com/avsystem/justworks/core/gen/ApiResponseGenerator.kt Generates HttpError/HttpErrorType and HttpSuccess<T> KotlinPoet files.
core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Generates per-tag Ktor client classes and endpoint suspend functions.
core/src/main/kotlin/com/avsystem/justworks/core/gen/InlineSchemaDeduplicator.kt Implements inline schema structural deduplication and collision handling.
core/src/main/kotlin/com/avsystem/justworks/core/gen/ModelGenerator.kt Generates models (data classes, sealed interfaces, type aliases), polymorphic serializers, and inline schema classes.
core/src/main/kotlin/com/avsystem/justworks/core/gen/NameUtils.kt Provides casing/name utilities and Kotlin keyword escaping.
core/src/main/kotlin/com/avsystem/justworks/core/gen/Names.kt Centralizes KotlinPoet ClassName/MemberName references used by generators.
core/src/main/kotlin/com/avsystem/justworks/core/gen/SerializersModuleGenerator.kt Generates SerializersModule registrations for sealed hierarchies.
core/src/main/kotlin/com/avsystem/justworks/core/gen/TypeMapping.kt Maps intermediate TypeRef to KotlinPoet TypeName.
core/src/main/kotlin/com/avsystem/justworks/core/model/ApiSpec.kt Defines intermediate API model (spec/endpoints/schemas/enums/type refs).
core/src/main/kotlin/com/avsystem/justworks/core/model/TypeRef.kt Defines TypeRef hierarchy and primitive types.
core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecParser.kt Parses OpenAPI/Swagger into intermediate model; handles combinators/refs/wrapper patterns.
core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecValidator.kt Validates parsed OpenAPI input and surfaces issues/warnings.
core/src/test/kotlin/com/avsystem/justworks/core/gen/ApiResponseGeneratorTest.kt Tests for HttpError/HttpSuccess code generation.
core/src/test/kotlin/com/avsystem/justworks/core/gen/ClientGeneratorTest.kt Tests client generation (methods, params, auth header, error handling, polymorphism wiring).
core/src/test/kotlin/com/avsystem/justworks/core/gen/InlineSchemaDedupTest.kt Tests inline schema deduplication and name collision behavior.
core/src/test/kotlin/com/avsystem/justworks/core/gen/ModelGeneratorPolymorphicTest.kt Tests oneOf/anyOf/discriminator handling, polymorphic serializer generation, and allOf interactions.
core/src/test/kotlin/com/avsystem/justworks/core/gen/ModelGeneratorTest.kt Tests model generation (data classes, enums, defaults, keyword escaping, type aliases).
core/src/test/kotlin/com/avsystem/justworks/core/gen/NameUtilsTest.kt Tests casing, enum constant naming, Kotlin identifier escaping, and operation naming.
core/src/test/kotlin/com/avsystem/justworks/core/gen/SerializersModuleGeneratorTest.kt Tests serializers module generation for sealed hierarchies.
core/src/test/kotlin/com/avsystem/justworks/core/gen/TypeMappingTest.kt Tests TypeRef → KotlinPoet type mapping.
core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserPolymorphicTest.kt Tests parsing behavior for polymorphic and allOf specs.
core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecParserTest.kt Tests core parsing, $ref resolution, error reporting, anyOf rules, and v2 conversion.
core/src/test/kotlin/com/avsystem/justworks/core/parser/SpecValidatorTest.kt Tests validator behavior for missing required info.
core/src/test/resources/anyof-spec.yaml Fixture for anyOf parsing/generation behavior.
core/src/test/resources/anyof-valid-spec.yaml Fixture for anyOf + discriminator parsing behavior.
core/src/test/resources/invalid-spec.yaml Fixture for validator/parser failure behavior.
core/src/test/resources/mixed-combinator-spec.yaml Fixture ensuring mixed combinators raise an error.
core/src/test/resources/petstore-v2.json Swagger v2 fixture for conversion testing.
core/src/test/resources/petstore.yaml OpenAPI v3 fixture for baseline parsing tests.
core/src/test/resources/polymorphic-spec.yaml Polymorphism fixture (oneOf, discriminator, allOf).
core/src/test/resources/refs-spec.yaml Fixture for exercising various $ref resolution patterns.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +123 to +128
val builder = CodeBlock
.builder()
.add("%T·{\n", HTTP_CLIENT)
.indent()
.add("install(%T)·{\n", CONTENT_NEGOTIATION)
.indent()

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

The CodeBlock templates in buildClientInitializer contain literal '·' characters (e.g., "%T·{", "expectSuccess·=·false"). These will be emitted into the generated Kotlin source and will not compile; use normal spaces/newlines and let KotlinPoet handle indentation instead of embedding this marker character.

Copilot uses AI. Check for mistakes.
Comment on lines +334 to +339
builder.addStatement(
"%S·in·element.%M -> %T.serializer()",
uniqueField,
JSON_OBJECT_EXT,
variantClassName,
)

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

The generated when branch uses literal '·' characters in the statement format string ("%S·in·element..."), which will be emitted into the generated Kotlin code and cause compilation failures. Remove these marker characters and rely on KotlinPoet formatting/indentation.

Copilot uses AI. Check for mistakes.
Comment on lines +340 to +347
} else {
builder.addStatement(
"// No unique discriminating fields found for variant '$variantName'",
)
builder.addStatement(
"else -> TODO(%S)",
"No unique discriminating fields found for variant '$variantName' of anyOf '$parentName' - manual selectDeserializer required",
)

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

else -> TODO(...) is emitted inside the loop over variants, which can produce multiple else branches in the same when (invalid Kotlin) and also makes later variant checks unreachable. Emit variant-specific conditions in the loop and add a single final else branch after the loop (either TODO or a SerializationException).

Copilot uses AI. Check for mistakes.
Comment thread core/build.gradle.kts Outdated
}
val compileKotlin: KotlinCompile by tasks
compileKotlin.compilerOptions {
freeCompilerArgs.set(listOf("-Xcontext-parameters"))

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

freeCompilerArgs.set(listOf("-Xcontext-parameters")) overwrites any existing compiler args configured by other plugins/tasks. Prefer appending (e.g., add) to preserve defaults and avoid surprising build changes as the build grows.

Suggested change
freeCompilerArgs.set(listOf("-Xcontext-parameters"))
freeCompilerArgs.add("-Xcontext-parameters")

Copilot uses AI. Check for mistakes.
code.addStatement(
"append(%T.Authorization, %P)",
HTTP_HEADERS,
CodeBlock.of($$"Bearer ${'$'}{$tokenProvider}"),

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

This header value CodeBlock uses $$"..." which is not valid Kotlin syntax, so the generator itself will not compile. Build the "Bearer " string using standard Kotlin string/CodeBlock formatting, and reference the tokenProvider property correctly (e.g., as a lambda invocation if that's the intent).

Suggested change
CodeBlock.of($$"Bearer ${'$'}{$tokenProvider}"),
CodeBlock.of("\"Bearer ${'$'}{tokenProvider()}\""),

Copilot uses AI. Check for mistakes.
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/gen/ClientGenerator.kt Outdated
Comment on lines +539 to +544
PrimitiveType.DATE_TIME -> {
// Validate at generation time
try {
Instant.parse(prop.defaultValue as String)
"kotlin.time.Instant.parse(\"${prop.defaultValue}\")"
} catch (e: Exception) {

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

For DATE_TIME defaults, the generator both validates with Instant.parse(...) and emits kotlin.time.Instant.parse(...) into generated code. This will not compile and also doesn't match the repo's datetime dependency (kotlinx-datetime). Use kotlinx.datetime.Instant.parse(...) consistently for validation and emitted default values.

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +163
isPrimitiveOnly(schema) -> {
// For primitive-only schemas, generate type alias
// TODO: Extend SchemaModel to include primitiveType field for primitive-only schemas
// For now, defaulting to String as the most common case
listOf(generateTypeAlias(schema, STRING))

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

Primitive-only schemas are currently emitted as typealias <Name> = String regardless of the actual OpenAPI primitive type/format. This will generate incorrect models for type: integer/number/boolean/.... Either extend the parsed model to retain the primitive type for such schemas or avoid type-alias generation until that information is available.

Copilot uses AI. Check for mistakes.
Comment thread core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecParser.kt Outdated
@halotukozak
halotukozak changed the base branch from master to feat/model-parser March 11, 2026 19:55
@halotukozak
halotukozak changed the base branch from feat/model-parser to master March 12, 2026 14:17
@halotukozak
halotukozak changed the base branch from master to feat/model-parser March 12, 2026 14:18
halotukozak and others added 9 commits March 12, 2026 15:24
…usage

- Replace `Closeable` with `ApiClientBase` to streamline client structure.
- Switch to `safeCall` for standardized error handling.
- Implement `toResult` and `toEmptyResult` for consistent response processing.
- Refactor tests to reflect APIClient changes and shared helpers.
- Simplified schema handling in `ModelGenerator` and related test files.
- Updated tests to reflect removal of `isEnum` for cleaner and more maintainable code.
- Adjusted polymorphic schema generation to streamline logic and avoid redundant checks.
…licity

- Removed redundant `primitiveTypeName` method by merging its logic directly into `toTypeName`.
- Streamlined handling of `PrimitiveType` variants and improved code clarity.
…ions across core modules

- Consolidated function parameter formatting into single lines where applicable for cleaner and more consistent code style.
- Updated relevant test files and utilities to match revised formatting.
… clarity and consistency

- Removed `Generator` placeholder object and unused tests for backtick-escaped keywords handling.
- Replaced `generateSuccess` with `generateHttpSuccess` in `ApiResponseGenerator`.
- Simplified string utilities in `NameUtils`, removing hardcoded keyword checks.
- Streamlined schema and model generation logic in `ModelGenerator`.
- Refactored related tests
…rove readability across core generators

- Removed `generateTo` methods from `ApiResponseGenerator` and `ClientGenerator` for unused file-writing logic.
- Simplified parameter grouping and processing in `ClientGenerator` using cleaner mappings and utility methods.
- Consolidated inline schema resolution in `ModelGenerator` to use a worklist for more efficient and readable logic.
- Adjusted formatting and applied constants for improved consistency across modules.
@halotukozak

Copy link
Copy Markdown
Contributor Author

will be divided into several smaller PRs

@halotukozak
halotukozak deleted the feat/generators branch March 19, 2026 11:02
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.

2 participants