You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add OpenApiMockResponsePlugin to mock APIs from OpenAPI specs - #2036
specFile can be a local path (relative to the config file, watched for changes) or an http(s) URL. JSON and YAML specs are supported.
Behavior
Requests are matched against spec paths (path parameters act as wildcards) and operations. Watched requests that don't match the spec pass through to the real API.
The response status is the lowest 2xx response defined for the operation, then default, otherwise 200.
The response body comes from the first available source:
Examples defined in the spec (media type example/examples)
The configured language model (global languageModel setting), prompted with the request, the operation description, and a simplified schema so values are realistic (for example, givenName and mail)
Values generated from the schema: example → default → enum → format-aware value → type-based value. $ref, allOf, oneOf, and anyOf are followed, arrays get one item, and recursion is cut off.
Language model output is validated as JSON and normalized to the expected type (array or object). Invalid output falls back to the schema-based values.
Generated bodies are cached in memory per operation (method + path template), so subsequent requests don't call the model again. The cache is cleared when the spec changes or the proxy restarts.
Adds CORS headers when the request has an Origin header and merges RateLimitingPlugin headers.
Other changes
New prompt: DevProxy/prompts/openapi_mock_response.prompty
…otnet#531
Generates responses from spec examples, the configured language model,
or schema-based fallback values, and caches them per operation.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unresolved critical cache-isolation and path/server-matching defects can serve stale or incorrect mock bodies or prevent valid OpenAPI requests from being mocked.
- Use ordinal cache keys and publish spec + cache as one snapshot
- Prefer concrete paths over templated ones when matching requests
- Return JSON null for null-only schemas
- Accept scalar language model responses and validate scalar types
- Keep generated numbers within minimum/maximum without overflow
- Don't cache failed generations
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Schema composition handling drops required nested and enclosing properties and ignores later non-object constraints, allowing invalid fallback responses to be generated and cached.
No open issues 🎉
3 previously missed issues
In code that hasn't changed since last review
Preserve enclosing schema constraints when selecting a branch
Selecting a oneOf or anyOf branch discards the enclosing schema's properties and constraints. For example, an object with a required id property and a branch defining name generates only {"name":"string"} when the language model is unavailable, omitting the required id. Generate from the combination of the enclosing schema and the selected branch rather than the branch alone.
Combine allOf constraints when generating non-object samples
For non-object allOf schemas, retaining the first generated value ignores constraints in later branches. With allOf: [{type: integer}, {type: integer, minimum: 1}], the branches generate 0 and 1, but this returns 0, which violates the response schema and is then cached. Combine the branches' constraints before generating a scalar or array sample instead of selecting the first branch's value.
Recursively merge overlapping objects in allOf schemas
When two allOf branches define required fields under the same object property—for example, profile.givenName and profile.mail—this assignment replaces the entire first profile object. The fallback then returns only mail and caches a response missing givenName; the same helper also loses nested properties in the language-model schema description. Recursively merge overlapping objects instead of replacing them.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unresolved schema-generation, validation, and serialization issues can produce and cache invalid mock responses, including violated allOf constraints, incorrect null handling, and JSON bodies mislabeled as XML.
For an operation whose only response media type is application/xml and whose schema is an object, GetMediaType selects XML but this serializes the sample as JSON. The client receives a JSON body labeled as XML and cannot parse the mocked response. Serialize according to the selected media type, or pass through unsupported encodings instead of returning a malformed mock.
Reject non-null model output for null-only schemas
A null-only schema reaches this default branch, so any non-null JSON returned by the language model is accepted and cached. This bypasses the corrected schema fallback that emits the JSON value null. Reject model output for the "null" expected type so it falls back to that valid body.
Merge allOf constraints before generating shared property values
Generating each allOf branch independently and then merging the samples can violate earlier branches. For example, two object branches defining count as an integer with minimum: 1 and maximum: 10 generate {"count":1} and {"count":0}; this merge returns {"count":0}, which violates the combined schema and is cached as the mock response. Combine constraints for shared properties before generating their values rather than overwriting previously generated scalar values.
Keeping only the first enum does not satisfy allOf: branches enum: [a, b] and enum: [b, c] generate a, although only b is permitted. Intersect enum constraints using JSON value equality, handle empty intersections explicitly, and ensure selected examples or defaults satisfy the intersection.
A null sample can be a valid array item, not just a recursion cutoff. For type: array, items: { type: "null" }, and minItems: 1, this returns [] instead of [null], producing a response outside the schema. Preserve the item when its schema explicitly has the null type.
Preserve stricter constraints when combining allOf schemas
The simplified schema treats allOf constraints as last-write-wins: integer branches with minimum: 10 followed by minimum: 1 describe a minimum of 1 instead of 10. Model responses satisfying only that weaker bound pass the top-level type check and are cached. Preserve the conjunction when describing allOf, including constraints on shared properties, rather than overwriting constraints in branch order.
- Pass through media types that can't be generated, like XML, unless
the spec defines a string example
- Skip the language model for null-only schemas
- Resolve nested allOf when combining constraints
- Keep null array items for null-only item schemas
- Keep the stricter minimum/maximum when describing allOf schemas
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Responses to the issues listed in the review summaries (they weren't posted as threads):
Fixed:
oneOf/anyOf now keep the enclosing schema's properties (3b676de).
Non-object allOf combines constraints from all branches, including nested allOf (3b676de, cb0e4c2).
Overlapping objects in allOf are merged recursively, and required lists are combined (3b676de).
Non-JSON media types: when the spec has no string example (for example, an XML schema without an example), requests now pass through instead of getting a JSON body labeled as XML (cb0e4c2).
Null-only schemas skip the language model and return null (cb0e4c2).
The schema description sent to the model keeps the stricter minimum/maximum across allOf, including on shared properties (cb0e4c2).
Not changing:
Merge allOf constraints for shared property values and intersect enums across allOf: the schema-based fallback is meant to produce basic, best-effort values for when no language model is available. Fully solving allOf constraints (combining per-property constraints, intersecting enums, checking examples and defaults against the result) means building a JSON Schema constraint solver, which is out of scope here. These are corner cases, and users can always add explicit examples to the spec, which take precedence.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human review is needed to assess omitted findings involving lost schema constraints and response-format mismatches that can produce invalid mocked responses.
- Generate text/* bodies only from string schemas or string examples,
pass other text responses through
- Include length, pattern, multipleOf and array constraints in the
schema sent to the language model
- Generate minItems items and honor multipleOf in fallback samples
- Add tests
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Follow-up on the latest review summary (it had no inline threads):
Fixed in this push:
text/* response format: a text/* body is now generated only from a string schema or a string example. Other text responses, like an object schema under text/plain, pass through. This matches the existing handling of non-JSON media types without an example.
Constraints in the prompt: the schema sent to the language model now includes minLength/maxLength, pattern, multipleOf, minItems/maxItems and uniqueItems, and the prompt asks the model to respect them. Validating model output against the full schema will be tracked in a separate issue.
Fallback generator: it now generates minItems items (capped by maxItems) and honors multipleOf. pattern stays best-effort.
Added OpenApiMockResponsePluginTests covering the above.
Not changing:
default-only responses return 200:default has no status code, so 200 is a reasonable, predictable choice for a mock. Responses with an explicit 2xx status code still use that code.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The generator drops schema constraints, produces duplicate items in unique arrays, and can stall requests with billions of increments, while AI completion and fallback behavior remain untested.
…ApiMockResponsePlugin
- Keep multipleOf, minItems, maxItems, uniqueItems and pattern when combining allOf
- Compute integer-compatible multiples directly instead of searching
- Generate distinct items for uniqueItems arrays (best-effort)
- Test language model output handling and caching with a fake client
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Re: the latest review summary ("enum handling and allOf merging can weaken schema constraints"). This repeats findings we already pushed back on in an earlier round. The schema-based generator is a best-effort fallback, used only when there's no spec example and no language model. Full enum intersection and per-property constraint merging across allOf are out of scope for it. Validating generated output against the schema is tracked as a separate follow-up. No further changes for this one.
This branch has not been deployed
No deployments
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
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.
Closes #531
Adds
OpenApiMockResponsePlugin, which mocks an API based on its OpenAPI spec.Configuration
{ "plugins": [ { "name": "OpenApiMockResponsePlugin", "enabled": true, "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll", "configSection": "openApiMock" } ], "openApiMock": { "specFile": "openapi.yaml" } }specFilecan be a local path (relative to the config file, watched for changes) or an http(s) URL. JSON and YAML specs are supported.Behavior
default, otherwise 200.example/examples)languageModelsetting), prompted with the request, the operation description, and a simplified schema so values are realistic (for example,givenNameandmail)$ref,allOf,oneOf, andanyOfare followed, arrays get one item, and recursion is cut off.Originheader and merges RateLimitingPlugin headers.Other changes
DevProxy/prompts/openapi_mock_response.promptyschemas/v4.1.0/openapimockresponseplugin.schema.json(targets v4.1.0, in line with Add per-mock rate to MockResponsePlugin #2027)Testing
Tested manually with a detached proxy against a sample spec: