Skip to content

feat(core): nest inline types in their owner + PascalCase component names (#92)#94

Open
halotukozak wants to merge 9 commits into
masterfrom
feat/92-nested-inline-types
Open

feat(core): nest inline types in their owner + PascalCase component names (#92)#94
halotukozak wants to merge 9 commits into
masterfrom
feat/92-nested-inline-types

Conversation

@halotukozak

@halotukozak halotukozak commented Jun 10, 2026

Copy link
Copy Markdown
Member

Closes #92.

Reworks how inline (anonymous) schemas are generated and normalizes type names — three parts, one coherent change:

1. Nest inline operation bodies in the client class

Inline request/response bodies were flat top-level files with names like Domains_updateRequest. They now nest in the owning client class: DomainsApi.DomainsUpdateRequest / DomainsApi.DomainsUpdateResponse. Naming = PascalCase(operationId) + Request/Response (no tag-strip heuristic — operationId is author-controlled); non-2xx / default get distinct suffixes.

2. Unified, ownership-aware inline pass

A single planInlineTypes replaces the two old mechanisms (collectAllInlineSchemas + the operation-only planner). It lifts every inline object (operation bodies and object properties, recursively) into PlannedInlineType trees and rewrites the spec to references. Object properties now nest under their parent type too (Pet.Address). No structural dedup — each occurrence is named/placed relative to its owner. TypeRef.Inline lost its stringly-typed contextHint; placement/naming is now purely a generation concern. Removed InlineSchemaKey, InlineTypeResolver, collectInlineTypeRefs.

3. PascalCase component type names

Component schema / enum / typealias names are emitted as PascalCase identifiers (dataSetListDataSetList), resolved consistently via Hierarchy.classNameFor so $refs agree. Wire identity untouched (@SerialName / discriminator keep spec names).

Tests

End-to-end (CodeGeneratorTest): operation-body nesting, property nesting, lowercase→PascalCase with $ref resolution. Full core suite + plugin functional green; obsolete inline/dedup tests removed.

🤖 Generated with Claude Code

halotukozak and others added 2 commits June 10, 2026 11:05
Inline (anonymous) request/response body schemas were generated as
flat, structurally-deduplicated top-level model files with awkward names
like `Domains_updateRequest`. They are now lifted per operation and
generated as data classes nested inside the owning client class, e.g.
`DomainsApi.DomainsUpdateRequest` / `DomainsApi.DomainsUpdateResponse`.

- New `planOperationInlineTypes` pass (run by CodeGenerator) rewrites
  inline operation bodies into per-operation reference ids — no
  structural dedup, so each operation gets its own copy.
- `Hierarchy` resolves those ids to the nested `ClassName`.
- `ClientGenerator` nests the data classes in the client class and
  registers the ids before generating functions, so signatures resolve.
- Naming: `PascalCase(operationId)` + `Request`/`Response` (no tag
  stripping — operationId is author-controlled and arbitrary); non-2xx
  and default responses get distinct suffixes.

Inline objects nested in model properties (-> nested under the parent
type) and PascalCasing of top-level component names are follow-ups.

Refs #92

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 10, 2026

Copy link
Copy Markdown

Coverage Report

Overall Project 96.47% -0.14% 🍏
Files changed 98.84% 🍏

File Coverage
TypeRef.kt 100% 🍏
ClientGenerator.kt 99.81% 🍏
ResolvedApiSpec.kt 99.67% -0.33% 🍏
Hierarchy.kt 99.12% 🍏
ModelGenerator.kt 95.87% -0.2% 🍏
SpecParser.kt 93.81% -0.23% 🍏
CodeGenerator.kt 88.61% 🍏
Utils.kt 81.44% -1.52%

halotukozak and others added 2 commits June 10, 2026 11:32
…line too

Replaces the two separate inline mechanisms (ModelGenerator's
collectAllInlineSchemas + the operation-only planner) with a single
ownership-aware pass, planInlineTypes, that lifts every inline object
schema — operation request/response bodies AND object-typed properties,
recursively — into PlannedInlineType trees and rewrites the spec to
references.

- Parser: TypeRef.Inline no longer carries a stringly-typed contextHint;
  inline detection is purely structural. Naming/placement is now a pure
  generation concern.
- Operation bodies nest in the client class (as before); object
  properties now nest in their parent data class (e.g. Pet.Address),
  recursively for nested inline objects.
- No structural dedup: each occurrence is named/placed relative to its
  owner. Removes InlineSchemaKey, InlineTypeResolver (resolveInlineTypes),
  collectInlineTypeRefs.
- Shared recursive emitter ModelGenerator.emitNestedInline registers
  reference ids -> nested ClassNames so resolution stays consistent.

Refs #92

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Component schema, enum, and typealias type names are now emitted as
PascalCase Kotlin identifiers (e.g. dataSetList -> DataSetList,
data_owner -> DataOwner), resolved consistently through a single
Hierarchy.classNameFor so $ref usages, nested-variant lookups, and the
get() fallback all agree.

Wire identity is untouched: @SerialName / @JsonClassDiscriminator and
discriminator mapping keep the original spec names; only the Kotlin
identifier changes (a non-polymorphic type name never appears on the
wire).

Closes #92

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@halotukozak halotukozak changed the title feat(core): nest inline operation body types inside the client class feat(core): nest inline types in their owner + PascalCase component names (#92) Jun 10, 2026
@halotukozak halotukozak added the enhancement New feature or request label Jun 10, 2026
halotukozak and others added 5 commits June 10, 2026 13:43
Replace the two id-keyed inline maps (clientInline/modelInline) with a
gen-layer TransformedApiSpec whose endpoints and schemas carry their own
lifted inline types (TransformedEndpoint/TransformedSchema). This removes
the soft foreign keys between the spec and the inline maps, drops the
Hierarchy.modelInline field and the ClientGenerator operationInlineTypes
parameter, and keeps inline placement local to its owner.

The single-pass planner is now the ApiSpec.transform() extension, with its
mutually recursive walk deduplicated into plan()/planProperties()/collect().

Fix a latent bug: request bodies were lifted for every content type, so
form/multipart inline bodies became a Reference and their properties no
longer expanded into individual function parameters. Only JSON bodies are
nested now; form/multipart bodies stay inline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ne-types

# Conflicts:
#	core/src/main/kotlin/com/avsystem/justworks/core/gen/InlineSchemaKey.kt
#	core/src/main/kotlin/com/avsystem/justworks/core/gen/InlineTypeResolver.kt
#	core/src/main/kotlin/com/avsystem/justworks/core/gen/model/ModelGenerator.kt
#	core/src/main/kotlin/com/avsystem/justworks/core/model/TypeRef.kt
#	core/src/main/kotlin/com/avsystem/justworks/core/parser/SpecParser.kt
#	core/src/test/kotlin/com/avsystem/justworks/core/gen/InlineSchemaDedupTest.kt
…port master features

Reconcile the merge from master: keep the nested-inline architecture and rename
its types coherently — ApiSpec.resolveInlines() returns a ResolvedApiSpec whose
ResolvedEndpoint/ResolvedSchema carry NestedType (sealed Obj/Enum) trees.

Ported from master onto this architecture:
- inline enums generated as enums nested in their owner (incl. oneOf/anyOf variants)
- uniqueItems -> Set, honor property defaults, empty response -> Unit
- stripDiscriminatorProperties to suppress orphan discriminator enums

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ne-types

# Conflicts:
#	core/src/main/kotlin/com/avsystem/justworks/core/gen/CodeGenerator.kt
#	core/src/main/kotlin/com/avsystem/justworks/core/gen/client/ClientGenerator.kt
#	core/src/main/kotlin/com/avsystem/justworks/core/gen/model/ModelGenerator.kt
#	core/src/test/kotlin/com/avsystem/justworks/core/gen/ClientGeneratorTest.kt
#	core/src/test/kotlin/com/avsystem/justworks/core/gen/IntegrationTest.kt
#	core/src/test/kotlin/com/avsystem/justworks/core/gen/ModelGeneratorInlineEnumTest.kt
#	core/src/test/kotlin/com/avsystem/justworks/core/gen/ModelGeneratorPolymorphicTest.kt
#	core/src/test/kotlin/com/avsystem/justworks/core/gen/ModelGeneratorRegressionTest.kt
#	core/src/test/kotlin/com/avsystem/justworks/core/gen/ModelGeneratorTest.kt
# Conflicts:
#	core/src/test/kotlin/com/avsystem/justworks/core/gen/ClientGeneratorTest.kt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generated type names are not normalized to PascalCase (e.g. dataSetList, Perform-searchRequest)

1 participant