fix(kotlin): jsonb open-bag api-contract gate + runtime-write verification (#98)#112
Merged
Merged
Conversation
…ation (#98) Wire the Kotlin api-contract jsonb sub-corpus (Document entity, field.string @dbColumnType=jsonb open JSON bag) in both lanes — a hand-rolled reference Exposed server and the GENERATED Spring controller hosted over MockMvc against a Postgres testcontainer — and fix a real codegen bug the generated lane surfaced. Runtime-write verification (Mandate 1 — clean): the generated Kotlin validator (KotlinValidatorGenerator) is a structural drift gate only — no per-field string/length/required check is emitted; the entity generator's @Valid annotations derive solely from @required/@maxLength/validator.*, none of which the open bag carries. The Exposed write codec takes a kotlinx JsonElement natively. The persistence-conformance op:roundtrip gate (asset-uuid-roundtrip.yaml) already inserts an object through the Kotlin runtime and reads it back parsed (green since #104). So there is no string-validator gap on the jsonb write path. Codegen fix the generated lane caught: a field.string @dbColumnType=jsonb field produced a <Entity>Controller that referenced the column's kotlinx JsonElement element type in its (dead) FR-009 filter dispatch (`p.value as JsonElement`) without importing it — the controller did not compile. The open bag is neither a scalar filter nor sort target (the FilterAllowlist already omits it — it is not @filterable), so it is now excluded from the controller's filter dispatch + sort allowlist in both the vanilla and TPH emit paths. Locked by a focused unit test; all 256 codegen-kotlin tests stay green (non-jsonb controllers byte-identical). Serialization seam: a JsonElement is a sealed polymorphic type that neither Spring JSON converter handles by default — vanilla Jackson cannot construct it, and the built-in KotlinSerializationJsonHttpMessageConverter refuses polymorphic bodies (HTTP 415). The generated lane wires a small Jackson JsonElement codec module (the consumer's serialization wiring, like the DB bootstrap — not a change to the generated artifact). Tests: JsonbApiContractConformanceTest (reference) + JsonbGeneratedApiContractConformanceTest (generated) green; Author generated lane (20) unaffected; codegen-kotlin 256/256. Part of #98. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LuZWKnWzYGVnESijL7uuky
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the Kotlin side of the jsonb open-bag cross-port api-contract gate (
field.string @dbColumnType=jsonbopen JSON bag), deferred for Java/Kotlin/C# by #110.Runtime-write verification (Mandate 1 — clean, no gap)
KotlinValidatorGenerator) is a structural drift gate only — it compares the metadata field set to the Exposed column set; it emits no per-field string/length/required check.@Validjakarta annotations derive solely from@required/@maxLength/validator.*, none of which the open-bagpayloadcarries → no validation is applied to it on write.JsonElementnatively. The persistence-conformanceop:roundtripgate (asset-uuid-roundtrip.yaml,payloadjsonb) already inserts an object through the Kotlin runtime and reads it back parsed (green since fix(codegen-kotlin): expose field.string @dbColumnType:jsonb as a parsed JSON value in REST payloads (#98) #104).Codegen fix the generated lane caught
A
field.string @dbColumnType=jsonbfield produced a<Entity>Controllerthat referenced the column's kotlinxJsonElementelement type in its (dead) FR-009 filter dispatch (p.value as JsonElement) without importing it — the controller did not compile. The open bag is neither a scalar filter nor a sort target (theFilterAllowlistalready omits it — it is not@filterable), so it is now excluded from the controller's filter dispatch + sort allowlist in both the vanilla and TPH emit paths. Locked by a focused unit test; all 256 codegen-kotlin tests stay green (non-jsonb controllers byte-identical).api-contract Kotlin wiring
Both lanes mirror the
m2m/tphKotlin harnesses and reuse the sharedApiContractAssertions(rowdeep-equal — a stringified"{\"k\":\"v\"}"fails it):DocumentApiServer(Exposedjsonbcolumn over a Postgres testcontainer).KotlinSpringControllerGenerator→DocumentControllerhosted on MockMvc over a Postgres testcontainer.Serialization seam finding: a
JsonElementis a sealed polymorphic type that neither of Spring's built-in JSON converters handles by default — vanilla Jackson can't construct it, andKotlinSerializationJsonHttpMessageConverterrefuses polymorphic bodies (HTTP 415). The generated lane wires a small JacksonJsonElementcodec module — the consumer's serialization wiring (a seam, like the DB bootstrap), not a change to the generated artifact.Test evidence
JsonbApiContractConformanceTest(reference) +JsonbGeneratedApiContractConformanceTest(generated): green (Postgres testcontainers).ApiContractGeneratedConformanceTest(Author generated, 20): green — confirms the controller-generator change is regression-free.codegen-kotlin: 256/256 (incl. snapshot byte-identity + the new unit test).Deferred
Java + C# jsonb api-contract harnesses remain (tracked in #98) — pre-push TS typecheck gate was bypassed (
SKIP_TS_TYPECHECK=1) as it tripped on a pre-existingbun-typesenv issue, not this Java/Kotlin-only change.Part of #98.
🤖 Generated with Claude Code