Epic: OpenAPI migration and environment removal (0.2.0)
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
Research direction
This is a tracking epic, not a self-contained code task. Start with the linked issues in the stated dependency order, beginning with the OpenAPI loader and the files named in the summary, including NetworkMockScreen.kt, NetworkMockViewModel.kt, MockConfiguration.kt, and MockConfigRepository.kt. Done means the independently reviewable linked issues are completed and the 0.2.0 migration is integrated.
Written by the indexing model from the issue text.
Description
Summary
devview-networkmock-core currently configures mocks with a bespoke mocks.json format built around an API groups × environments model. This epic tracks replacing that with OpenAPI 3.x as the configuration format, and removing the environment axis in favor of a simpler mocked/live split plus API version awareness.
This issue is a tracking issue only — no code changes here. Each linked issue is independently implementable and reviewable.
Why
-
The format is bespoke. Integrators hand-write
mocks.jsoneven though most already own an OpenAPI spec describing the same paths, methods, operation ids, and example responses — plus thingsmocks.jsoncannot express (response headers, declared status codes, parameter schemas). -
Environments are the wrong axis. A running app talks to exactly one base URL at a time (whichever its build points at). Modelling staging and production simultaneously means:
- the UI renders a tab per group×environment, so roughly half the tabs are dead weight for any given build — toggling a mock in the "staging" tab does nothing when the app is built against "prod" (
devview-networkmock/src/commonMain/kotlin/com/worldline/devview/networkmock/NetworkMockScreen.kt:151-165,viewmodel/NetworkMockViewModel.kt:91-104) environmentIdis baked into the DataStore key (EndpointKey.compositeKey,devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/model/MockConfiguration.kt:374-387), duplicating persisted stateenvironmentIdis also a response-file directory tier (repository/MockConfigRepository.kt:448-449), duplicating response files on disk- response-file discovery runs
groups × environments × endpoints(devview-networkmock/src/commonMain/kotlin/com/worldline/devview/networkmock/viewmodel/NetworkMockViewModel.kt:161-183), so the environment axis multiplies an already expensive eager-load loop
The shipped sample proves the axis is being misused for something else:
sample/network/src/commonMain/composeResources/files/networkmocks/mocks.json:72-77uses theprodenvironment'sendpointOverridesto rewrite/api/v1/profile/{userId}→/api/v2/profile/{userId}. That is an API version difference wearing an environment costume. The real requirements are: mocked vs. live (already covered by the existing global toggle) plus API versioning. - the UI renders a tab per group×environment, so roughly half the tabs are dead weight for any given build — toggling a mock in the "staging" tab does nothing when the app is built against "prod" (
Outcome
mocks.json and the environment concept are deleted in a breaking 0.2.0 release. Configuration becomes one OpenAPI 3.x document per API group (JSON or YAML). Operations carry a derived version tag (parsed from the path) for display and filtering only — the engine always responds to whatever version the app actually calls.
Key decisions (apply to every linked issue)
| Decision | Choice |
|---|---|
| Config format | OpenAPI 3.x, JSON and YAML. One spec file = one group. info.title → group name, servers[].url → hosts, paths → operations, operationId → operation id, summary → display name. No manifest file — a group spanning multiple API versions is just multiple paths entries in the same spec (e.g. both /api/v1/profile/{id} and /api/v2/profile/{id}). |
| Response bodies | Stay as external files on disk, referenced from the spec via the standard OpenAPI examples.<name>.externalValue field. No vendor extension needed for this. |
| Versioning | Version is a derived, display-only tag, parsed from the operation's path using a configurable regex (default /v(\d+)), exposed as a constructor parameter the same way responseSuffixes is today. Version is not part of the operation identity — /api/v1/x and /api/v2/x already have distinct operationIds in the spec, so they are naturally distinct operations. |
| Version behaviour | Observed only, for 0.2.0. The engine responds to whatever version the app actually calls. A v2 mock can sit unused until the app (or a feature flag) starts calling v2. Forcing/rewriting the app onto a specific version is a deliberately deferred, separate feature (#85 below) because it would mutate real network traffic and needs its own design pass. |
| Parser depth | Staged. The core parser (JSON + YAML, local #/components refs and external file refs, examples) blocks 0.2.0 since it must fully replace mocks.json. Schema-synthesised response bodies, requestBody matching, and allOf/oneOf/discriminator resolution are follow-up issues in 0.2.x — they add depth but nothing in 0.2.0 depends on them. |
| Migration | Big-bang breaking change at 0.2.0. No dual-format period — this is a pre-1.0 library (gradle.properties version 0.1.4), so a single clean break is preferable to maintaining two parsers and two DataStore key shapes indefinitely. A migration guide and a conversion script ship alongside the break. |
| Naming | Adopt OpenAPI vocabulary only for types that model something OpenAPI describes (endpoint → operation, group → spec). Types that model DevView's own runtime mocking behaviour, which OpenAPI has no concept of, keep their current names — see the naming table in #75. |
| Vendor extensions | Runtime-only behavior vanilla OpenAPI can't express (delay today, failure-rate/sequences as follow-ups) lives under an x-devview object — OpenAPI's standard x--prefixed Specification Extensions mechanism. See #94. |
| Loading strategy | Discovery returns metadata only (status code + example name) for the main list; response body content loads lazily, only for the operation whose detail screen is actually open. See #98. |
| Format extensibility | MockConfigRepository (#73) is a pure RawBytes -> ApiSpec seam — no OpenAPI-specific type may leak past it. This keeps a second, lower-ceremony input format (#99) cheap to add later, without deciding to build it now. |
Resulting model shape
OperationKey(specId, operationId) // was EndpointKey(groupId, environmentId, endpointId)
compositeKey = "$specId-$operationId" // DataStore key shape changes -> needs a one-shot prune, see #78
ApiSpec = one OpenAPI document
id <- info.title (slugified)
name <- info.title
servers <- servers[].url, hostnames extracted // replaces EnvironmentConfig.url
Operation
operationId <- operationId
name <- summary
path <- paths key
method <- operation key
version <- regex over path, nullable, display-only
responses <- responses.<code>.content.*.examples.<name>.externalValue
Linked issues
0.2.0 (blocking, in dependency order)
- #73 —
feat: OpenAPI 3.x spec loader (JSON + YAML) - #74 —
refactor: remove the environment axis - #75 —
refactor: adopt OpenAPI vocabulary across the networkmock API - #76 —
feat: derive response variants from declared OpenAPI examples - #98 —
perf: load response bodies lazily instead of eager whole-config discovery - #77 —
feat: API version tag with configurable path regex - #94 —
feat: x-devview vendor extension for delay simulation - #78 —
fix: prune orphaned DataStore keys after the key-shape change - #79 —
feat: rework the NetworkMock UI for specs and versions - #80 —
docs: migrate the sample app and all networkmock docs to OpenAPI - #81 —
docs: migration guide + mocks.json -> OpenAPI conversion script
0.2.x follow-ups (OpenAPI depth, not blocking)
- #82 —
feat: synthesise response bodies from OpenAPI schemas - #83 —
feat: requestBody matching - #84 —
feat: allOf / oneOf / discriminator resolution
Deferred capability (explicitly out of scope for 0.2.0)
- #85 —
feat: force the app onto a given API version
Deferred, demand-gated (raised during external design review, tracked deliberately — not scheduled)
- #95 —
feat: probabilistic failure-rate injection - #96 —
feat: stateful / sequential mocks - #97 —
spike: feasibility study for build-time OpenAPI codegen - #99 —
spike/feat: minimal low-ceremony JSON frontend for integrators without a spec— pick up only on demonstrated adoption friction, not speculatively
Independent of the migration (found during the audit, can land any time)
- #86 —
fix: gate networkmock logging - #87 —
feat: serve response headers declared in the spec - #88 —
feat: simulate network failures - #89 —
refactor: make MockHttpClientCall internal - #90 —
fix: allow spec reload - #91 —
test: cover the gaps left by the audit - #92 —
fix: remove dead preview stubs in NetworkMockEndpointScreen
Suggested order of work
#73 → #74 → #75 → #76 → #98 → #77 → #94 → (#78, #79 in parallel) → (#80, #81 last, since docs follow the API per this repo's documentation-hygiene rule in CLAUDE.md).
#75 (the rename) is sequenced early so that PRs for #76 onward are written directly in the final vocabulary instead of being renamed twice. #98 (lazy loading) lands before #79 (UI rework) so the UI is built against the final loading model once, not reworked twice. #94 (delay vendor extension) depends only on #73 and can move earlier if convenient — it's placed here to keep the "config model settles, then behavior extensions layer on" reading order.
#86–#92 have no dependency on the migration and can be picked up by anyone, at any time, independently. #95–#97 and #99 are the same — deliberately tracked, explicitly not scheduled; #99 additionally requires a demonstrated need before it's picked up at all (see its issue body).
Revision note (2026-07-30)
This epic's scope was revised after discussing the migration with an external AI (Gemini) as a design-partner sounding board. That review's central recommendation — keep the bespoke JSON format as DevView's permanent runtime core, treat OpenAPI as an optional adapter — was considered and rejected: it re-opens the dual-format question already closed in favor of the big-bang break, and its supporting arguments (OpenAPI can't express runtime behavior; in-app overrides need a lightweight model) don't hold up against this codebase specifically, since OperationMockState already separates runtime mock state from parsed config, and the "can't express runtime behavior" gap applies equally to the old bespoke format. No existing issue was closed, reversed, or reduced in scope as a result.
Two real gaps and one good architectural point did survive the review, and are reflected above: the delay-simulation feature needed an explicit home in the new format (#94), whole-config eager loading needed to not carry into a world where specs can be much larger (#98), and the parser-as-a-pure-seam design (already implicit in #73) is now an explicit, checked requirement specifically because it's what keeps a second input format (#99) cheap to add later without deciding to build it now.
- Dominant language
- Kotlin
- Stars
- 10
- Forks
- 2
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 13
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from worldline/devview
-
enhancement good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 25/100
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 45/100
All issues in worldline/devview
Similar issues
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Richie97/track-history#325 ·
-
bug CLI documentation reliability
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
KW-related Type:bug UI / UX
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
RevenueCat/purchases-android#4299 · 1 comment ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
AAswordman/Operit#1265 · 3 comments ·