@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.
Files changed (3) hide show
  1. package/README.md +57 -1
  2. package/index.js +29 -25
  3. 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; if a generated mock route conflicts with an already registered route, the existing route wins. Registering an implemented route after a conflicting mock route causes Fastify to reject the duplicate route.
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 ?? {}).forEach(([exampleName, example]) => {
166
- rejectInvalidRequestMatchLocation(example, operationName)
165
+ Object.entries(param.examples ?? {})
166
+ .filter(([, example]) => example.externalValue === undefined)
167
+ .forEach(([exampleName, example]) => {
168
+ rejectInvalidRequestMatchLocation(example, operationName)
167
169
 
168
- const requestExampleVariants = requestExamples.get(exampleName) ?? [[]]
170
+ const requestExampleVariants = requestExamples.get(exampleName) ?? [[]]
169
171
 
170
- requestExampleVariants.forEach(conditions => {
171
- conditions.push({ source, name: param.name, value: example.value })
172
- })
172
+ requestExampleVariants.forEach(conditions => {
173
+ conditions.push({ source, name: param.name, value: example.value })
174
+ })
173
175
 
174
- requestExamples.set(exampleName, requestExampleVariants)
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 ?? {}).forEach(([exampleName, example]) => {
183
- rejectInvalidRequestMatchLocation(example, operationName)
184
+ Object.entries(media.examples ?? {})
185
+ .filter(([, example]) => example.externalValue === undefined)
186
+ .forEach(([exampleName, example]) => {
187
+ rejectInvalidRequestMatchLocation(example, operationName)
184
188
 
185
- const existingVariants = requestExamples.get(exampleName) ?? [[]]
189
+ const existingVariants = requestExamples.get(exampleName) ?? [[]]
186
190
 
187
- // Body examples attach to the parameter-only variant for the same name.
188
- const parameterConditions = existingVariants.find(conditions =>
189
- conditions.every(condition => condition.source !== 'body')
190
- ) ?? existingVariants[0].filter(condition => condition.source !== 'body')
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
- // Preserve existing body variants while adding this media type variant.
193
- const requestExampleVariants = [
194
- ...existingVariants.filter(conditions => conditions.some(condition => condition.source === 'body')),
195
- [
196
- ...parameterConditions,
197
- { source: 'body', mediaType, value: example.value },
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
- requestExamples.set(exampleName, requestExampleVariants)
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.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.8.5"
22
+ "fastify": "^5.0.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@fastify/cookie": "^11.0.2",