Skip to content

Commit 1bc7966

Browse files
committed
feat: add reference openapi mock apiproxy implementation
1 parent 1242751 commit 1bc7966

17 files changed

+7878
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@
66
.vscode
77
.DS_Store
88
*.jar
9+
*.iml
10+
.idea

CODEOWNERS

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
/references/recaptcha-enterprise @joelgauci
2626
/references/southbound-mtls @danistrebel
2727
/references/threat-protect @joelgauci
28+
/references/openapi-mock @micovery
2829
/tools/apigee-envoy-quickstart @ganadurai
2930
/tools/apigee-openlegacy @tomfi @joelgauci
3031
/tools/apigee-sackmesser @danistrebel

README.md

+17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
# Apigee DevRel
2+
<!--
3+
Copyright 2024 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
217

318
[![In Solidarity](https://github.com/jpoehnelt/in-solidarity-bot/raw/main/static//badge-flat.png)](https://github.com/apps/in-solidarity)
419

@@ -59,6 +74,8 @@ further to fit a particular use case.
5974
API protection against bot leveraging reCAPTCHA enterprise
6075
- [Firestore Facade](references/firestore-facade) - Reference implementation
6176
for a long term caching/storage solution based on Cloud Firestore
77+
- [OpenAPI Mock](references/openapi-mock) - Reference implementation
78+
for creating a mock API proxy from an OpenAPI 3 specification
6279

6380
## Tools
6481

references/openapi-mock/README.md

+215
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
# OpenAPI Mock API Proxy
2+
<!--
3+
Copyright 2024 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
18+
This is a [reference API Proxy implementation](./apiproxy) that lets you easily create a mock API proxy from your OpenAPI 3 specification, allowing you to simulate API behavior without a real backend.
19+
20+
The reference mock API proxy supports the following features.
21+
22+
* [CORS](#cors) (using Apigee [CORS](https://cloud.google.com/apigee/docs/api-platform/reference/policies/cors-policy) policy)
23+
* [Request Validation](#request-validation) (using Apigee [OASValidation](https://cloud.google.com/apigee/docs/api-platform/reference/policies/oas-validation-policy))
24+
* [Dynamic Response Status Code](#dynamic-response-status-code) (using a custom [JavaScript](https://cloud.google.com/apigee/docs/api-platform/reference/policies/javascript-policy) policy)
25+
* [Dynamic Response Content Type](#dynamic-response-content-type) (using a custom [JavaScript](https://cloud.google.com/apigee/docs/api-platform/reference/policies/javascript-policy) policy)
26+
* [Dynamic Response Body](#dynamic-response-body) (using a custom [JavaScript](https://cloud.google.com/apigee/docs/api-platform/reference/policies/javascript-policy) policy)
27+
28+
29+
## Customizing The Mock API Proxy
30+
31+
This [reference implementation](./apiproxy) provides a solid foundation for building your own mock API proxy. You can customize it by adding your own policies, modifying the existing configuration, and using your own OpenAPI specification. This is a great way to learn about Apigee or to achieve more advanced customizations.
32+
33+
At the very minimum, you have to:
34+
35+
1. Update the `<BasePath>/v3/petstore</BasePath>` element within the [default.xml](./apiproxy/proxies/default.xml) proxy endpoint.
36+
2. Replace the included sample [spec.json](./apiproxy/resources/oas/spec.json) file with your own OpenAPI 3 spec file.
37+
38+
> The OpenAPI 3 spec file has to be in JSON format.
39+
> This is so that it can be used by the main JavaScript policy.
40+
41+
42+
Then, you can use the [Apigee CLI](https://github.com/apigee/apigeecli/releases/) tool, and run the following command to deploy the mock API proxy. e.g.
43+
44+
```shell
45+
APIGEE_ORG="target-apigee-org-name"
46+
APIGEE_ENV="target-apigee-env"
47+
TOKEN="$(gcloud auth print-access-token)"
48+
49+
apigeecli apis create bundle \
50+
--name my-apiproxy-v1 \
51+
--proxy-folder ./apiproxy \
52+
--org "${APIGEE_ORG}" \
53+
--env "${APIGEE_ENV}" \
54+
--ovr \
55+
--token "${TOKEN}" \
56+
--wait
57+
```
58+
59+
**Looking for a faster way?**
60+
61+
If you just need to quickly generate a mock API proxy from your OpenAPI 3 spec, the [apigee-go-gen](https://apigee.github.io/apigee-go-gen/installation/) tool can help.
62+
63+
The tool's [mock oas](https://apigee.github.io/apigee-go-gen/mock/mock-openapi-spec/) command automates the process, saving you time and effort.
64+
65+
Here is an example of how to generate a mock API proxy using the `apigee-go-gen` tool.
66+
67+
```shell
68+
apigee-go-gen mock oas \
69+
--input ./examples/specs/oas3/petstore.yaml \
70+
--output ./out/mock-apiproxies/petstore.zip
71+
```
72+
It is that simple. All the information needed to generate the mock API proxy is derived from the input spec itself.
73+
74+
Under the hood, it is using the same reference implementation JavaScript policy from this repo.
75+
76+
Finally, you just as shown before, you would use the [Apigee CLI](https://github.com/apigee/apigeecli/releases/) to deploy the API proxy bundle.
77+
78+
## Mock API Proxy Features
79+
80+
See below for more details on each feature.
81+
82+
### CORS
83+
84+
The reference mock API proxy makes it easy to test your API from various browser-based clients.
85+
86+
Here's how it works:
87+
88+
* **Automatic CORS Headers:** The proxy automatically adds the necessary CORS headers (like `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, etc.) to all responses.
89+
90+
* **Preflight Requests:** The proxy correctly handles preflight `OPTIONS` requests, responding with the appropriate CORS headers to indicate allowed origins, methods, and headers.
91+
92+
* **Permissive Configuration:** By default, the CORS policy is configured to be as permissive as possible, allowing requests from any origin with any HTTP method and headers. This maximizes flexibility for your testing.
93+
94+
The CORS policy ensures that your mock API behaves like a real API in a browser environment, simplifying your development and testing workflow.
95+
96+
### Request Validation
97+
98+
The reference mock API proxy validates the incoming requests against your specification.
99+
This ensures that the HTTP headers, query parameters, and request body all conform to the defined rules.
100+
101+
This helps you catch errors in your client code early on.
102+
103+
You can disable request validation by passing the header:
104+
105+
```
106+
Mock-Validate-Request: false
107+
```
108+
109+
### Dynamic Response Status Code
110+
111+
The reference mock API proxy generates different status codes for your mock API responses. Here's how it works:
112+
113+
* **Prioritizes success:** If the [operation](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.1.md#operation-object) allows `HTTP 200` status code, the proxy will use it.
114+
* **Random selection:** If `HTTP 200` is not allowed for a particular operation, the proxy will pick a random status code from those allowed.
115+
116+
**Want more control?** You can use headers to select response the status code:
117+
118+
* **Specific status code:** Use the `Mock-Status` header in your request and set it to the desired code (e.g., `Mock-Status: 404`).
119+
* **Random status code:** Use the `Mock-Fuzz: true` header to get a random status code from your spec.
120+
121+
If you use both `Mock-Status` and `Mock-Fuzz`, `Mock-Status` takes precedence.
122+
123+
### Dynamic Response Content-Type
124+
125+
The reference mock API proxy automatically selects the `Content-Type` for responses. Here is how it works:
126+
127+
* **JSON preferred:** If the operation allows `application/json`, the proxy will default to using it.
128+
* **Random selection:** If `application/json` is not available, the proxy will randomly choose from the media types available for that operation.
129+
130+
**Want more control?** You can use headers to select the response Content-Type:
131+
132+
* **Standard `Accept` header:** You can use the standard `Accept` header in your request to request a specific media type (e.g., `Accept: application/xml`).
133+
* **Random media type:** Alternatively, use the `Mock-Fuzz: true` header to have the proxy select a random media type the available ones.
134+
135+
If you use both `Accept` and `Mock-Fuzz`, the `Accept` header will take precedence.
136+
137+
138+
### Dynamic Response Body
139+
140+
The reference mock API proxy generates realistic response bodies based on your OpenAPI spec.
141+
142+
Here's how it determines what to send back for any particular operation's response (in order):
143+
144+
1. **Prioritizes `example` field:** If the response includes an `example` field, the proxy will use that example.
145+
146+
2. **Handles multiple `examples`:** If the response has an `examples` field with multiple examples, the proxy will randomly select one. You can use the `Mock-Example` header to specify which example you want (e.g., `Mock-Example: my-example`).
147+
148+
3. **Uses schema examples:** If no response examples are provided, but the schema for the response has an `example`, the proxy will use that.
149+
150+
4. **Generates from schema:** As a last resort, the proxy will generate a random example based on the response schema. This works for JSON, YAML, and XML.
151+
152+
You can use the `Mock-Fuzz: true` header to force the proxy to always generate a random example from the schema, even if other static examples are available.
153+
154+
155+
### Repeatable API Responses
156+
157+
The reference mock API proxy uses a special technique to make its responses seem random, while still allowing you to get the same response again if needed. Here's how it works:
158+
159+
* **Pseudo-random numbers:** The "random" choices the proxy makes (like status codes and content) are actually generated using a pseudo-random number generator (PRNG). This means the responses look random, but are determined by a starting value called a "seed."
160+
161+
* **Unique seeds:** Each request uses a different seed, so responses vary. However, the seed is provided in a special response header called `Mock-Seed`.
162+
163+
* **Getting the same response:** To get an identical response, simply include the `Mock-Seed` header in a new request, using the value from a previous response. This forces the proxy to use the same seed and generate the same "random" choices, resulting in an identical response.
164+
165+
This feature is super helpful for:
166+
167+
* **Testing:** Ensuring your tests always get the same response.
168+
* **Debugging:** Easily recreating specific scenarios to pinpoint issues in application code.
169+
170+
Essentially, by using the `Mock-Seed` header, you can control the randomness of the mock API responses, making them repeatable for testing and debugging.
171+
172+
### Example Generation from JSON Schemas
173+
174+
The following fields are supported when generating examples from a JSON schema:
175+
176+
* `$ref` - local references are followed
177+
* `$oneOf` - chooses a random schema
178+
* `$anyOf` - chooses a random schema
179+
* `$allOf` - combines all schemas
180+
* `object` type
181+
* `required` field - all required properties are chosen
182+
* `properties` field - a random set of properties is chosen
183+
* `additionalProperties` field - only used when there are no `properties` defined
184+
* `array` type
185+
* `minItems`, `maxItems` fields - array length chosen randomly between these values
186+
* `items` field - determines the type of array elements
187+
* `prefixItems` (not supported yet)
188+
* `null` type
189+
* `const` type
190+
* `boolean` type - true or false randomly chosen
191+
* `string` type
192+
* `enum` field - a random value is chosen from the list
193+
* `pattern` field (not supported yet)
194+
* `format` field
195+
* `date-time` format
196+
* `date` format
197+
* `time` format
198+
* `email` format
199+
* `uuid` format
200+
* `uri` format
201+
* `hostname` format
202+
* `ipv4` format
203+
* `ipv6` format
204+
* `duration` format
205+
* `minLength`, `maxLength` fields - string length chosen randomly between these values
206+
* `integer` type
207+
* `minimum`, `maximum` fields - a random integer value chosen randomly between these values
208+
* `exclusiveMinimuim` field (boolean, JSON-Schema 4)
209+
* `exclusiveMaximum` field (boolean, JSON-Schema 4)
210+
* `multipleOf` field
211+
* `number` type
212+
* `minimum`, `maximum` fields - a random float value chosen randomly between these values
213+
* `exclusiveMinimuim` field (boolean, JSON-Schema 4)
214+
* `exclusiveMaximum` field (boolean, JSON-Schema 4)
215+
* `multipleOf` field
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<!--
3+
Copyright 2024 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<APIProxy revision="5" name="petstore-v3">
18+
<DisplayName>Swagger Petstore - OpenAPI 3.0</DisplayName>
19+
<Description>This is a sample Pet Store Server based on the OpenAPI 3.0 specification.</Description>
20+
<CreatedAt>1730258534837</CreatedAt>
21+
<LastModifiedAt>1730258534837</LastModifiedAt>
22+
<BasePaths>/v3/petstore</BasePaths>
23+
<Policies>
24+
<Policy>AM-LoadSpec</Policy>
25+
<Policy>AM-SetError</Policy>
26+
<Policy>CORS-Allow</Policy>
27+
<Policy>JS-MockResponse</Policy>
28+
<Policy>OAS-Validate</Policy>
29+
</Policies>
30+
<ProxyEndpoints>
31+
<ProxyEndpoint>default</ProxyEndpoint>
32+
</ProxyEndpoints>
33+
<Resources>
34+
<Resource>jsc://response-mocker.cjs</Resource>
35+
<Resource>oas://spec.json</Resource>
36+
</Resources>
37+
</APIProxy>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<!--
3+
Copyright 2024 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<AssignMessage name="AM-LoadSpec">
18+
<AssignVariable>
19+
<Name>spec_json</Name>
20+
<ResourceURL>oas://spec.json</ResourceURL>
21+
</AssignVariable>
22+
</AssignMessage>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<!--
3+
Copyright 2024 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<AssignMessage continueOnError="false" enabled="true" name="AM-SetError">
18+
<DisplayName>AM-SetError</DisplayName>
19+
<Properties/>
20+
<Set>
21+
<Payload contentType="application/json">{
22+
"error": "{escapeJSON(error.message)}"
23+
}</Payload>
24+
<StatusCode>{error.status.code}</StatusCode>
25+
<ReasonPhrase>{error.reason.phrase}</ReasonPhrase>
26+
</Set>
27+
</AssignMessage>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<!--
3+
Copyright 2024 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<CORS continueOnError="false" enabled="true" name="CORS-Allow">
18+
<DisplayName>CORS-Allow</DisplayName>
19+
<AllowOrigins>{request.header.origin}</AllowOrigins>
20+
<AllowMethods>GET, PUT, POST, DELETE, OPTIONS</AllowMethods>
21+
<AllowHeaders>*</AllowHeaders>
22+
<ExposeHeaders>*</ExposeHeaders>
23+
<MaxAge>3628800</MaxAge>
24+
<AllowCredentials>true</AllowCredentials>
25+
<GeneratePreflightResponse>true</GeneratePreflightResponse>
26+
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
27+
</CORS>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<!--
3+
Copyright 2024 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<Javascript async="false" continueOnError="false" enabled="true" timeLimit="5000" name="JS-MockResponse">
18+
<DisplayName>JS-MockResponse</DisplayName>
19+
<ResourceURL>jsc://response-mocker.cjs</ResourceURL>
20+
</Javascript>

0 commit comments

Comments
 (0)