diff --git a/samples/openapi-mock-responses/.devproxy/contoso-api.yaml b/samples/openapi-mock-responses/.devproxy/contoso-api.yaml new file mode 100644 index 00000000..a36d7ba3 --- /dev/null +++ b/samples/openapi-mock-responses/.devproxy/contoso-api.yaml @@ -0,0 +1,190 @@ +openapi: 3.0.3 +info: + title: Contoso Store API + description: Sample API used to demonstrate generating mock responses from an OpenAPI spec with Dev Proxy. + version: 1.0.0 +servers: + - url: https://api.contoso.com/v1 +paths: + /products: + get: + operationId: listProducts + summary: List products + responses: + '200': + description: List of products + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Product' + example: + - id: 1 + name: Contoso Trail Backpack + description: Water-resistant 30L backpack for day hikes. + price: 89.99 + currency: USD + inStock: true + - id: 2 + name: Contoso Insulated Bottle + description: Stainless steel bottle that keeps drinks cold for 24 hours. + price: 24.5 + currency: USD + inStock: false + post: + operationId: createProduct + summary: Create a product + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + responses: + '201': + description: Product created + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + '400': + description: Invalid product + /products/{productId}: + get: + operationId: getProduct + summary: Get a product + parameters: + - name: productId + in: path + required: true + schema: + type: integer + responses: + '200': + description: Product + content: + application/json: + schema: + $ref: '#/components/schemas/Product' + '404': + description: Product not found + delete: + operationId: deleteProduct + summary: Delete a product + parameters: + - name: productId + in: path + required: true + schema: + type: integer + responses: + '204': + description: Product deleted + /customers: + get: + operationId: listCustomers + summary: List customers + responses: + '200': + description: List of customers + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Customer' + /customers/{customerId}/orders: + get: + operationId: listCustomerOrders + summary: List orders for a customer + parameters: + - name: customerId + in: path + required: true + schema: + type: string + responses: + '200': + description: List of orders + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Order' +components: + schemas: + Product: + type: object + properties: + id: + type: integer + name: + type: string + description: + type: string + price: + type: number + format: double + currency: + type: string + inStock: + type: boolean + Customer: + type: object + properties: + id: + type: string + format: uuid + firstName: + type: string + lastName: + type: string + email: + type: string + format: email + phone: + type: string + city: + type: string + country: + type: string + createdAt: + type: string + format: date-time + Order: + type: object + properties: + id: + type: string + customerId: + type: string + format: uuid + status: + type: string + enum: + - pending + - shipped + - delivered + - cancelled + orderDate: + type: string + format: date-time + total: + type: number + format: double + items: + type: array + items: + type: object + properties: + productId: + type: integer + productName: + type: string + quantity: + type: integer + unitPrice: + type: number + format: double diff --git a/samples/openapi-mock-responses/.devproxy/devproxyrc.json b/samples/openapi-mock-responses/.devproxy/devproxyrc.json new file mode 100644 index 00000000..fc70b4d9 --- /dev/null +++ b/samples/openapi-mock-responses/.devproxy/devproxyrc.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/rc.schema.json", + "plugins": [ + { + "name": "OpenApiMockResponsePlugin", + "enabled": true, + "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll", + "configSection": "openApiMock" + } + ], + "urlsToWatch": [ + "https://api.contoso.com/*" + ], + "openApiMock": { + "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/openapimockresponseplugin.schema.json", + "specFile": "contoso-api.yaml" + }, + "languageModel": { + "enabled": true + }, + "logLevel": "information", + "newVersionNotification": "stable" +} diff --git a/samples/openapi-mock-responses/.vscode/extensions.json b/samples/openapi-mock-responses/.vscode/extensions.json new file mode 100644 index 00000000..8f6c4300 --- /dev/null +++ b/samples/openapi-mock-responses/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "garrytrinder.dev-proxy-toolkit" + ] +} diff --git a/samples/openapi-mock-responses/README.md b/samples/openapi-mock-responses/README.md new file mode 100644 index 00000000..0e3137e3 --- /dev/null +++ b/samples/openapi-mock-responses/README.md @@ -0,0 +1,133 @@ +# Mock an API from its OpenAPI spec + +Mock an entire API from its OpenAPI spec, with realistic responses generated by a local language model when the spec has no examples + +![Dev Proxy returning mock responses generated from an OpenAPI spec](assets/screenshot.png) + +## Summary + +This sample shows how to mock an API using only its OpenAPI spec with Dev Proxy's `OpenApiMockResponsePlugin`. You don't need to write mock files. Dev Proxy reads the spec, matches incoming requests to its operations, and returns responses based on what the spec defines. + +The sample includes the OpenAPI spec for a fictional Contoso Store API (`https://api.contoso.com/v1`) with products, customers, and orders. Some operations include response examples in the spec. Others only define a schema. For those, Dev Proxy uses a local language model to generate realistic data. If no language model is available, it falls back to values based on the schema types. + +## Compatibility + +![Dev Proxy v4.1.0](https://aka.ms/devproxy/badge/v4.1.0) + +## Contributors + +- [Waldek Mastykarz](https://github.com/waldekmastykarz) + +## Version history + +| Version | Date | Comments | +| ------- | ---------------- | --------------- | +| 1.0 | October 31, 2026 | Initial release | + +## Prerequisites + +- [Dev Proxy](https://aka.ms/devproxy) v4.1.0 or later +- Optional: a local language model, such as [Ollama](https://ollama.com), to generate realistic responses. See [Use a local language model](https://learn.microsoft.com/microsoft-cloud/dev/dev-proxy/how-to/use-language-model) for setup instructions. + +## Minimal path to awesome + +- Get the sample: + - Download just this sample: + + ```bash + npx gitload-cli https://github.com/pnp/proxy-samples/tree/main/samples/openapi-mock-responses + ``` + + or + + - [Download as a .ZIP file](https://pnp.github.io/download-partial/?url=https://github.com/pnp/proxy-samples/tree/main/samples/openapi-mock-responses) and unzip it, or + - Clone this repository +- Open a terminal in the sample folder, for example, if you cloned the repository: `cd samples/openapi-mock-responses` +- Optionally, start your local language model, for example Ollama: + + ```bash + ollama serve + ``` + +- Start Dev Proxy with the configuration file from the sample: + + ```bash + devproxy + ``` + +- Send requests to the Contoso Store API, for example using curl: + + ```bash + # List products (response taken from the example in the spec) + curl -ikx http://127.0.0.1:8000 https://api.contoso.com/v1/products + + # Get a product (response generated from the Product schema) + curl -ikx http://127.0.0.1:8000 https://api.contoso.com/v1/products/42 + + # Create a product (returns 201, the lowest 2xx status code defined in the spec) + curl -ikx http://127.0.0.1:8000 -X POST https://api.contoso.com/v1/products \ + -H 'Content-Type: application/json' \ + -d '{"name":"Contoso Rain Jacket","price":129.99}' + + # Delete a product (returns 204 without a body) + curl -ikx http://127.0.0.1:8000 -X DELETE https://api.contoso.com/v1/products/42 + + # List customers (response generated from the Customer schema) + curl -ikx http://127.0.0.1:8000 https://api.contoso.com/v1/customers + + # List orders for a customer + curl -ikx http://127.0.0.1:8000 https://api.contoso.com/v1/customers/abc123/orders + ``` + +## Features + +Using this sample you can use Dev Proxy to: + +- Mock an API using its OpenAPI spec without writing any mock files +- Return responses based on examples defined in the spec +- Generate realistic responses with a local language model for operations without examples +- Fall back to schema-based values when no language model is available + +### How it works + +The sample's configuration (`.devproxy/devproxyrc.json`) enables the `OpenApiMockResponsePlugin` and points it to the OpenAPI spec using the `specFile` property: + +```json +"openApiMock": { + "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v4.1.0/openapimockresponseplugin.schema.json", + "specFile": "contoso-api.yaml" +} +``` + +`specFile` can be a path to a local JSON or YAML file, relative to the configuration file, or an http(s) URL. Dev Proxy watches the local spec file for changes and reloads it automatically. + +For each request to a URL in `urlsToWatch`, the plugin: + +1. Matches the request against the paths and operations in the spec. Path parameters, like `{productId}`, match any value. Requests that don't match any operation are passed through to the actual API. +1. Picks the response status code: the lowest 2xx status code, then `default`, and otherwise `200`. +1. Builds the response body, using the first available source: + 1. The example defined in the spec for the response. + 1. Data generated by the language model, based on the response schema and its property names. To enable this, the sample turns on the language model in the configuration (`"languageModel": { "enabled": true }`). By default, Dev Proxy uses Ollama running locally. + 1. Values generated based on the types in the schema. + +Generated responses are cached in memory per operation (method and path template), so you get consistent responses for the same operation until you restart Dev Proxy or change the spec. + +### Mock your own API + +To mock your own API, replace `contoso-api.yaml` with your API's OpenAPI spec, update the `specFile` property, and update `urlsToWatch` to match your API's URL. + +## Help + +We do not support samples, but this community is always willing to help, and we want to improve these samples. We use GitHub to track issues, which makes it easy for community members to volunteer their time and help resolve issues. + +You can try looking at [issues related to this sample](https://github.com/pnp/proxy-samples/issues?q=label%3A%22sample%3A%20openapi-mock-responses%22) to see if anybody else is having the same issues. + +If you encounter any issues using this sample, [create a new issue](https://github.com/pnp/proxy-samples/issues/new). + +Finally, if you have an idea for improvement, [make a suggestion](https://github.com/pnp/proxy-samples/issues/new). + +## Disclaimer + +**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.** + +![](https://m365-visitor-stats.azurewebsites.net/SamplesGallery/pnp-devproxy-openapi-mock-responses) diff --git a/samples/openapi-mock-responses/assets/sample.json b/samples/openapi-mock-responses/assets/sample.json new file mode 100644 index 00000000..191c4696 --- /dev/null +++ b/samples/openapi-mock-responses/assets/sample.json @@ -0,0 +1,73 @@ +[ + { + "name": "pnp-devproxy-openapi-mock-responses", + "source": "pnp", + "title": "Mock an API from its OpenAPI spec", + "shortDescription": "Mock an entire API from its OpenAPI spec, with realistic responses generated by a local language model when the spec has no examples", + "url": "https://github.com/pnp/proxy-samples/tree/main/samples/openapi-mock-responses", + "downloadUrl": "https://pnp.github.io/download-partial/?url=https://github.com/pnp/proxy-samples/tree/main/samples/openapi-mock-responses", + "longDescription": [ + "Mock an entire API from its OpenAPI spec, with realistic responses generated by a local language model when the spec has no examples" + ], + "creationDateTime": "2026-10-31", + "updateDateTime": "2026-10-31", + "products": [ + "Dev Proxy" + ], + "metadata": [ + { + "key": "SAMPLE ID", + "value": "openapi-mock-responses" + }, + { + "key": "PRESET", + "value": "No" + }, + { + "key": "MOCKS", + "value": "Yes" + }, + { + "key": "PLUGIN", + "value": "No" + }, + { + "key": "PROXY VERSION", + "value": "v4.1.0" + } + ], + "thumbnails": [ + { + "name": "screenshot.png", + "type": "image", + "order": 100, + "url": "https://raw.githubusercontent.com/pnp/proxy-samples/main/samples/openapi-mock-responses/assets/screenshot.png", + "alt": "Dev Proxy returning mock responses generated from an OpenAPI spec" + } + ], + "authors": [ + { + "gitHubAccount": "waldekmastykarz", + "pictureUrl": "https://github.com/waldekmastykarz.png", + "name": "Waldek Mastykarz" + } + ], + "references": [ + { + "name": "Get started with Dev Proxy", + "description": "Learn how to install and run Dev Proxy", + "url": "https://learn.microsoft.com/microsoft-cloud/dev/dev-proxy/get-started" + }, + { + "name": "Use a local language model", + "description": "Learn how to configure Dev Proxy to use a local language model", + "url": "https://learn.microsoft.com/microsoft-cloud/dev/dev-proxy/how-to/use-language-model" + }, + { + "name": "OpenAPI Specification", + "description": "The OpenAPI Specification used to describe HTTP APIs", + "url": "https://spec.openapis.org/oas/latest.html" + } + ] + } +]