@techspikes/fastify-mock-fallback 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -1
- package/index.js +29 -25
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ await fastify.register(fastifyMockFallback, {
|
|
|
53
53
|
await fastify.listen({ port: 3000 })
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Existing Fastify routes are not replaced. Register implemented routes before this plugin;
|
|
56
|
+
Existing Fastify routes are not replaced when they use the same HTTP method and route structure as a generated mock. Register implemented routes before this plugin; for example, a generated `GET /pet/:petId` mock is skipped when either `GET /pet/:petId` or `GET /pet/:id` is already registered. Registering an implemented route after a conflicting mock route causes Fastify to reject the duplicate route.
|
|
57
57
|
|
|
58
58
|
### Options
|
|
59
59
|
|
|
@@ -149,6 +149,60 @@ paths:
|
|
|
149
149
|
missing:
|
|
150
150
|
x-request-match: missing
|
|
151
151
|
value: { code: 404, message: not found }
|
|
152
|
+
post:
|
|
153
|
+
operationId: updatePetWithForm
|
|
154
|
+
parameters:
|
|
155
|
+
- in: path
|
|
156
|
+
name: petId
|
|
157
|
+
required: true
|
|
158
|
+
schema: { type: integer }
|
|
159
|
+
examples:
|
|
160
|
+
update-doggie:
|
|
161
|
+
value: 10
|
|
162
|
+
- in: query
|
|
163
|
+
name: name
|
|
164
|
+
schema: { type: string }
|
|
165
|
+
examples:
|
|
166
|
+
update-doggie:
|
|
167
|
+
value: doggie
|
|
168
|
+
- in: query
|
|
169
|
+
name: status
|
|
170
|
+
schema: { type: string }
|
|
171
|
+
examples:
|
|
172
|
+
update-doggie:
|
|
173
|
+
value: sold
|
|
174
|
+
responses:
|
|
175
|
+
"200":
|
|
176
|
+
description: successful operation
|
|
177
|
+
content:
|
|
178
|
+
application/json:
|
|
179
|
+
examples:
|
|
180
|
+
default:
|
|
181
|
+
x-request-match: update-doggie
|
|
182
|
+
value: { id: 10, name: doggie, status: sold }
|
|
183
|
+
/pet:
|
|
184
|
+
post:
|
|
185
|
+
operationId: addPet
|
|
186
|
+
requestBody:
|
|
187
|
+
description: Create a new pet
|
|
188
|
+
required: true
|
|
189
|
+
content:
|
|
190
|
+
application/json:
|
|
191
|
+
examples:
|
|
192
|
+
doggie:
|
|
193
|
+
value:
|
|
194
|
+
id: 10
|
|
195
|
+
name: doggie
|
|
196
|
+
status: available
|
|
197
|
+
responses:
|
|
198
|
+
"200":
|
|
199
|
+
description: successful operation
|
|
200
|
+
content:
|
|
201
|
+
application/json:
|
|
202
|
+
examples:
|
|
203
|
+
default:
|
|
204
|
+
x-request-match: doggie
|
|
205
|
+
value: { id: 10, name: doggie, status: available }
|
|
152
206
|
/pets:
|
|
153
207
|
get:
|
|
154
208
|
operationId: listPets
|
|
@@ -165,6 +219,8 @@ In this example:
|
|
|
165
219
|
* `GET /pet/1` uses a response example with `x-request-match`.
|
|
166
220
|
* `GET /pet/2` uses same-name matching.
|
|
167
221
|
* `GET /pet/3` uses a response example with `x-request-match` and returns `404`.
|
|
222
|
+
* `POST /pet/10?name=doggie&status=sold` matches its path and query examples, then returns the response selected by `x-request-match`.
|
|
223
|
+
* `POST /pet` with `Content-Type: application/json` and the `doggie` request body matches the request body example and returns its linked response.
|
|
168
224
|
* `GET /pets` has no request examples, so it uses the status `200` response `example`.
|
|
169
225
|
|
|
170
226
|
## Validation
|
package/index.js
CHANGED
|
@@ -162,44 +162,48 @@ function collectRequestExamples (pathItem, operation, operationName) {
|
|
|
162
162
|
const source = RequestMatcher.sources[param.in]
|
|
163
163
|
|
|
164
164
|
// Parameters without examples do not contribute matching conditions.
|
|
165
|
-
Object.entries(param.examples ?? {})
|
|
166
|
-
|
|
165
|
+
Object.entries(param.examples ?? {})
|
|
166
|
+
.filter(([, example]) => example.externalValue === undefined)
|
|
167
|
+
.forEach(([exampleName, example]) => {
|
|
168
|
+
rejectInvalidRequestMatchLocation(example, operationName)
|
|
167
169
|
|
|
168
|
-
|
|
170
|
+
const requestExampleVariants = requestExamples.get(exampleName) ?? [[]]
|
|
169
171
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
172
|
+
requestExampleVariants.forEach(conditions => {
|
|
173
|
+
conditions.push({ source, name: param.name, value: example.value })
|
|
174
|
+
})
|
|
173
175
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
requestExamples.set(exampleName, requestExampleVariants)
|
|
177
|
+
})
|
|
176
178
|
})
|
|
177
179
|
|
|
178
180
|
// Request body examples add body conditions to matching request examples.
|
|
179
181
|
Object.entries(operation.requestBody?.content ?? {}).forEach(([mediaType, media]) => {
|
|
180
182
|
rejectInvalidRequestMatchLocation(media, operationName)
|
|
181
183
|
|
|
182
|
-
Object.entries(media.examples ?? {})
|
|
183
|
-
|
|
184
|
+
Object.entries(media.examples ?? {})
|
|
185
|
+
.filter(([, example]) => example.externalValue === undefined)
|
|
186
|
+
.forEach(([exampleName, example]) => {
|
|
187
|
+
rejectInvalidRequestMatchLocation(example, operationName)
|
|
184
188
|
|
|
185
|
-
|
|
189
|
+
const existingVariants = requestExamples.get(exampleName) ?? [[]]
|
|
186
190
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
// Body examples attach to the parameter-only variant for the same name.
|
|
192
|
+
const parameterConditions = existingVariants.find(conditions =>
|
|
193
|
+
conditions.every(condition => condition.source !== 'body')
|
|
194
|
+
) ?? existingVariants[0].filter(condition => condition.source !== 'body')
|
|
191
195
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
// Preserve existing body variants while adding this media type variant.
|
|
197
|
+
const requestExampleVariants = [
|
|
198
|
+
...existingVariants.filter(conditions => conditions.some(condition => condition.source === 'body')),
|
|
199
|
+
[
|
|
200
|
+
...parameterConditions,
|
|
201
|
+
{ source: 'body', mediaType, value: example.value },
|
|
202
|
+
],
|
|
203
|
+
]
|
|
200
204
|
|
|
201
|
-
|
|
202
|
-
|
|
205
|
+
requestExamples.set(exampleName, requestExampleVariants)
|
|
206
|
+
})
|
|
203
207
|
})
|
|
204
208
|
|
|
205
209
|
return requestExamples
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techspikes/fastify-mock-fallback",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Fastify plugin that registers fallback mock routes from OpenAPI examples
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Fastify plugin that registers fallback mock routes from OpenAPI examples",
|
|
5
5
|
"author": "knzbr <87257088+knzbr@users.noreply.github.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"fastify-plugin": "^5.1.0"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"fastify": "^5.
|
|
22
|
+
"fastify": "^5.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@fastify/cookie": "^11.0.2",
|