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
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
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'sexternalValuepoints at the actual response body file on disk, using the standard OpenAPIExample Objectfield — 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 inDEFAULT_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 inDEFAULT_RESPONSE_SUFFIXES(5:"",-simple,-detailed,-error,-success—:150-156), it attempts to load a file named{endpointId}-{statusCode}{suffix}.jsonfrom two candidate directories (environment-tier then shared-tier,:462-486) and silently treats a failed load as "doesn't exist" (loadMockResponseFromPath,:588-602, catchesIllegalStateExceptionand returnsnull).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 viaString.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:
Discovery becomes: for each operation, iterate its declared
responses.<code>entries, and for each declared example, load the file atexternalValuevia the sameNetworkMockResourceLoaderalready used elsewhere. No probing, noDEFAULT_STATUS_CODES, noDEFAULT_RESPONSE_SUFFIXES, no filename-based status parsing.OperationMockState.Mockidentity changeToday,
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 derivesstatusCodefrom 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 nameddefault. Change the persisted shape to: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_CODESand thestatusCodesToDiscoverconstructor parameterMockConfigRepository.DEFAULT_RESPONSE_SUFFIXESand theresponseSuffixesconstructor parameterString.parseStatusCode()(devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/utils/MockFileNameUtils.kt) and its test (MockFileNameUtilsTest.kt)discoverResponseFiles/loadMockResponseprobing loops (repository/MockConfigRepository.kt:405-575) — replaced by declared-example iterationKeep
StatusCodeFamily(devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/model/StatusCodeFamily.kt) — this is used (sticky-header grouping inNetworkMockEndpointScreen.kt:119-121,:234-243, andEndpointStateChip), 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
responses/examplesfrom the parsed spec — zero probing, zero speculative resource loads for files that don't exist.externalValueresolution uses the existingNetworkMockResourceLoader.OperationMockState.Mockis 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.MockConfigRepositoryTest.kt:161-253to assert against declared examples instead of probed files.Files likely touched
devview-networkmock-core/src/commonMain/kotlin/com/worldline/devview/networkmock/core/repository/MockConfigRepository.ktdevview-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.ktdevview-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