feat: synthesise response bodies from OpenAPI schemas - #132
Open
MaxMichel2 wants to merge 1 commit into
Open
MaxMichel2 wants to merge 1 commit into
MaxMichel2 wants to merge 1 commit into
Conversation
MaxMichel2
force-pushed
the
feat/openapi-schema-synthesis
branch
from
September 23, 2026 14:02
3e7706b to
33185e4
Compare
MaxMichel2
added this pull request to stack #130
September 23, 2026 14:02
A status code with a declared content.<mediaType>.schema but no examples previously showed zero mockable variants. OpenApiParser now synthesizes one placeholder body per such media type instead, via a new internal SchemaSynthesizer: primitives, enum (first value), object/array (recursively), allOf (properties merged, conflicting definitions across members throw a clear error), and oneOf (first declared variant -- discriminator is parsed but doesn't yet steer variant selection, since there's no concrete request/response data at spec-parse time to disambiguate against). Deliberately narrow, not full JSON Schema conformance. SchemaObject/DiscriminatorObject added to OpenApiDocument.kt following the existing ref-capable-object style; ComponentsObject gains a schemas map; MediaTypeObject gains a schema field. ResolvedResponse's file-path-only content is generalised into a ResponseContent sealed interface (FromFile / Synthesized) so a synthesized body can flow through the same response index as a file-backed one. resolveResponseIndex's per-media-type example resolution is extracted into resolveMediaTypeResponses to keep it under detekt's LongMethod threshold. MockResponse gains isSynthesized: Boolean (default false, additive); the operation picker page shows a small "Generated" badge (MockItem.kt) on a synthesized response's row, styled after EndpointCard's method badge. api.txt regenerated for devview-networkmock-core; docs/modules/networkmock-core.md gains a "Schema-based response synthesis" section.
MaxMichel2
force-pushed
the
feat/openapi-schema-synthesis
branch
from
September 23, 2026 14:11
33185e4 to
d91afc8
Compare
This branch has not been deployed
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.
Summary
Stacked on #131 (PR chain: #123 → #124 → #127 → #128 → #129 → #126 → #131 → this) — merge in order.
The OpenAPI parser had no schema support at all — a status code with a declared
content.<mediaType>.schemabut noexamplesshowed zero mockable variants. This adds response-body synthesis for exactly that case, deliberately narrow (not full JSON Schema conformance): primitives,enum,object,array,allOf(merged), andoneOf(first variant) — seedocs/modules/networkmock-core.md's new "Schema-based response synthesis" section for the exact per-shape rules.What changed
SchemaObject/DiscriminatorObject(OpenApiDocument.kt): follow the existing$ref-capable-object style (HeaderObject/ParameterObject).ComponentsObjectgains aschemasmap;MediaTypeObjectgains aschemafield.SchemaSynthesizer(new file,openapi/SchemaSynthesizer.kt,internal object):synthesize(schema, resolveSchema)recursively resolves$refs via a caller-supplied callback (reusingOpenApiParser's own ref-resolution machinery from fix: resolve dollar-ref chains and disambiguate component sections #131 — a newParseContext.resolveSchemaresolves againstcomponents.schemas) and produces one plausibleJsonElementper schema shape.ResponseContent(new sealed interface,OpenApiParser.kt): generalizesResolvedResponse's file-path-only content intoFromFile/Synthesized, so a synthesized body flows through the same response index as a file-backed one, cached alongside it (no regeneration per discovery pass).resolveResponseIndex: per-media-type example resolution extracted into a newresolveMediaTypeResponseshelper (keeps the function under detekt'sLongMethodthreshold) — for each media type, declaredexamplesare resolved first; only if none are declared and aschemais present does synthesis kick in. A status code with any declared examples for a media type never falls back to synthesis for that media type, even if it also declares aschema.MockResponse.isSynthesized: Boolean(defaultfalse, additive): threaded throughMockConfigRepository.loadResponseFromPath. The operation picker page shows a small "Generated" badge (MockItem.kt) on a synthesized response's row, styled afterEndpointCard's method badge (Box+RoundedCornerShape(4.dp)+labelSmall).Scope decisions (per the handoff)
nullableis read but ignored — a real value is always synthesized, never JSONnull; this library mocks responses, it doesn't exercise null-handling.oneOf+discriminator: synthesizes the first declared variant regardless of whether adiscriminatoris present. There's no concrete request/response data at spec-parse time to disambiguate against, so full discriminator-based variant selection isn't attempted — noted here rather than built speculatively.allOfconflicts: two members declaring the same property with different schemas throws a clear error rather than silently picking one.allOf/oneOf) didn't turn out messier than expected against the test fixtures tried, so this shipped as scoped rather than being cut down to primitives/object/array only.Public API
api.txtregenerated fordevview-networkmock-coreviametalavaGenerateSignature— purely additive (MockResponsegains a defaulted constructor parameter, acomponent7(), and a getter/property;MockResponse.Companion.creategains a defaulted parameter).docs/modules/networkmock-core.mdupdated to match (new "Schema-based response synthesis" section; the$refresolution section already covered the reused ref machinery in #131).Tests
SchemaSynthesizerTest.kt(new, 17 tests): each primitive type,enum, nestedobject(including the no-explicit-type-but-propertiescase),array(including the no-itemserror case),allOfmerge (including the conflicting-members error case and the same-property-redeclared-identically non-conflict case),oneOfwith and withoutdiscriminator(both falling back to the first variant),$refresolution via the callback, and the no-recognizable-shape error case.MockConfigRepositoryTest.kt(+3 end-to-end wiring tests): a schema-only status code synthesizes a body withisSynthesized = true; a status code with bothexamplesand aschemaprefers the examples (isSynthesized = false); a$ref'd schema undercomponents.schemasresolves correctly before synthesizing.Verification
All green, including a full repo-wide
testAndroidHostTestrun to confirm no fallout outside the networkmock modules.🤖 Generated with GitHub Copilot