Skip to content

feat: derive response variants from declared OpenAPI examples #76

Description

@MaxMichel2

Part of #72

Depends on #1, #2, #3.

Summary

Replace the current probe-based response-file discovery with variants declared explicitly in the OpenAPI spec's responses.<code>.content.<mediaType>.examples. Each example's externalValue points at the actual response body file on disk, using the standard OpenAPI Example Object field — no vendor extension required.

Current state

MockConfigRepository.discoverResponseFiles() (devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/repository/MockConfigRepository.kt:438-501) has no declared list of what responses exist for an endpoint. Instead it probes: for every status code in DEFAULT_STATUS_CODES (15 codes: 200, 201, 202, 204, 400, 401, 403, 404, 409, 422, 429, 500, 502, 503, 504 — :135-142) crossed with every suffix in DEFAULT_RESPONSE_SUFFIXES (5: "", -simple, -detailed, -error, -success:150-156), it attempts to load a file named {endpointId}-{statusCode}{suffix}.json from two candidate directories (environment-tier then shared-tier, :462-486) and silently treats a failed load as "doesn't exist" (loadMockResponseFromPath, :588-602, catches IllegalStateException and returns null).

That's 15 × 5 = 75 resource-load attempts per endpoint, run eagerly for every endpoint in the config on screen load (devview-networkmock/src/commonMain/kotlin/com/worldline/devview/networkmock/viewmodel/NetworkMockViewModel.kt:161-183). The status code is then re-parsed back out of the successfully-loaded file name via String.parseStatusCode() (devview-networkmock-core/.../utils/MockFileNameUtils.kt, regex matching the last -{3 digits} segment).

What to build

An OpenAPI spec declares its responses — there's no need to probe:

responses:
  "200":
    content:
      application/json:
        examples:
          default:
            externalValue: responses/getUser/getUser-200.json
          detailed:
            externalValue: responses/getUser/getUser-200-detailed.json
  "404":
    content:
      application/json:
        examples:
          default:
            externalValue: responses/getUser/getUser-404.json

Discovery becomes: for each operation, iterate its declared responses.<code> entries, and for each declared example, load the file at externalValue via the same NetworkMockResourceLoader already used elsewhere. No probing, no DEFAULT_STATUS_CODES, no DEFAULT_RESPONSE_SUFFIXES, no filename-based status parsing.

OperationMockState.Mock identity change

Today, EndpointMockState.Mock(responseFile: String) (devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/model/NetworkMockState.kt:239-260) persists the response file name as its identity, and derives statusCode from it by re-parsing the name.

With externalValue, the file path is an implementation detail a spec author is free to move — the OpenAPI-addressable identity is the (statusCode, exampleName) pair. It must be both, not the example name alone: two different status codes can each have an example named default. Change the persisted shape to:

public data class Mock(val statusCode: Int, val exampleName: String) : OperationMockState

This is a DataStore-persisted shape change on top of the key-shape change already happening in #2/#6 — fold this into the same one-shot migration/prune in #6 rather than doing a second one.

Delete

  • MockConfigRepository.DEFAULT_STATUS_CODES and the statusCodesToDiscover constructor parameter
  • MockConfigRepository.DEFAULT_RESPONSE_SUFFIXES and the responseSuffixes constructor parameter
  • String.parseStatusCode() (devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/utils/MockFileNameUtils.kt) and its test (MockFileNameUtilsTest.kt)
  • The two-tier discoverResponseFiles/loadMockResponse probing loops (repository/MockConfigRepository.kt:405-575) — replaced by declared-example iteration

Keep

  • StatusCodeFamily (devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/model/StatusCodeFamily.kt) — this is used (sticky-header grouping in NetworkMockEndpointScreen.kt:119-121, :234-243, and EndpointStateChip), it just needs a status code integer, which declared responses still provide. Not dead code, don't remove it.
  • MockResponse.fromFile's display-name generation logic (model/MockResponse.kt:121-175) — still useful for turning (statusCode, exampleName) into a human-readable label; adapt its inputs rather than deleting it.

Acceptance criteria

  • Discovery reads declared responses/examples from the parsed spec — zero probing, zero speculative resource loads for files that don't exist.
  • externalValue resolution uses the existing NetworkMockResourceLoader.
  • OperationMockState.Mock is keyed by (statusCode, exampleName), not a file name string.
  • StatusCodeFamily-based grouping in the endpoint detail screen (NetworkMockEndpointScreen.kt) still works against the new model.
  • Old probing constants/functions are deleted, not deprecated.
  • Tests: port the discovery-ordering/dedup tests from MockConfigRepositoryTest.kt:161-253 to assert against declared examples instead of probed files.

Files likely touched

  • devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/repository/MockConfigRepository.kt
  • devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/model/NetworkMockState.kt (OperationMockState.Mock)
  • devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/model/MockResponse.kt
  • devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/utils/MockFileNameUtils.kt (deleted)
  • devview-networkmock-core/src/commonTest/.../utils/MockFileNameUtilsTest.kt (deleted)
  • devview-networkmock-core/src/commonTest/.../repository/MockConfigRepositoryTest.kt

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions