@furystack/rest-service 12.2.0 → 12.3.0

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 (74) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/esm/api-manager.d.ts +1 -1
  3. package/esm/api-manager.d.ts.map +1 -1
  4. package/esm/api-manager.js +3 -2
  5. package/esm/api-manager.js.map +1 -1
  6. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts +29 -0
  7. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts.map +1 -0
  8. package/esm/endpoint-generators/create-get-openapi-document-action.js +61 -0
  9. package/esm/endpoint-generators/create-get-openapi-document-action.js.map +1 -0
  10. package/esm/endpoint-generators/index.d.ts +2 -0
  11. package/esm/endpoint-generators/index.d.ts.map +1 -1
  12. package/esm/endpoint-generators/index.js +2 -0
  13. package/esm/endpoint-generators/index.js.map +1 -1
  14. package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts +23 -0
  15. package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts.map +1 -0
  16. package/esm/endpoint-generators/with-schema-and-openapi-action.js +2 -0
  17. package/esm/endpoint-generators/with-schema-and-openapi-action.js.map +1 -0
  18. package/esm/openapi/auth-provider-to-security-scheme.d.ts +14 -0
  19. package/esm/openapi/auth-provider-to-security-scheme.d.ts.map +1 -0
  20. package/esm/openapi/auth-provider-to-security-scheme.js +35 -0
  21. package/esm/openapi/auth-provider-to-security-scheme.js.map +1 -0
  22. package/esm/openapi/auth-provider-to-security-scheme.spec.d.ts +2 -0
  23. package/esm/openapi/auth-provider-to-security-scheme.spec.d.ts.map +1 -0
  24. package/esm/openapi/auth-provider-to-security-scheme.spec.js +42 -0
  25. package/esm/openapi/auth-provider-to-security-scheme.spec.js.map +1 -0
  26. package/esm/openapi/generate-openapi-document.d.ts +21 -0
  27. package/esm/openapi/generate-openapi-document.d.ts.map +1 -0
  28. package/esm/openapi/generate-openapi-document.js +144 -0
  29. package/esm/openapi/generate-openapi-document.js.map +1 -0
  30. package/esm/openapi/generate-openapi-document.spec.d.ts +2 -0
  31. package/esm/openapi/generate-openapi-document.spec.d.ts.map +1 -0
  32. package/esm/openapi/generate-openapi-document.spec.js +643 -0
  33. package/esm/openapi/generate-openapi-document.spec.js.map +1 -0
  34. package/esm/openapi/openapi-round-trip.advanced-api.json +363 -0
  35. package/esm/openapi/openapi-round-trip.crud-api.json +115 -0
  36. package/esm/openapi/openapi-round-trip.example-api.json +71 -0
  37. package/esm/openapi/openapi-round-trip.spec.d.ts +2 -0
  38. package/esm/openapi/openapi-round-trip.spec.d.ts.map +1 -0
  39. package/esm/openapi/openapi-round-trip.spec.js +525 -0
  40. package/esm/openapi/openapi-round-trip.spec.js.map +1 -0
  41. package/esm/swagger/generate-swagger-json.spec.js +1 -1
  42. package/esm/swagger/generate-swagger-json.spec.js.map +1 -1
  43. package/esm/validate.integration.spec.js +153 -32
  44. package/esm/validate.integration.spec.js.map +1 -1
  45. package/package.json +9 -9
  46. package/src/api-manager.ts +7 -3
  47. package/src/endpoint-generators/create-get-openapi-document-action.ts +96 -0
  48. package/src/endpoint-generators/index.ts +2 -0
  49. package/src/endpoint-generators/with-schema-and-openapi-action.ts +14 -0
  50. package/src/openapi/auth-provider-to-security-scheme.spec.ts +50 -0
  51. package/src/openapi/auth-provider-to-security-scheme.ts +41 -0
  52. package/src/openapi/generate-openapi-document.spec.ts +733 -0
  53. package/src/openapi/generate-openapi-document.ts +198 -0
  54. package/src/openapi/openapi-round-trip.advanced-api.json +363 -0
  55. package/src/openapi/openapi-round-trip.crud-api.json +115 -0
  56. package/src/openapi/openapi-round-trip.example-api.json +71 -0
  57. package/src/openapi/openapi-round-trip.spec.ts +621 -0
  58. package/src/swagger/generate-swagger-json.spec.ts +1 -1
  59. package/src/validate.integration.spec.ts +184 -33
  60. package/esm/endpoint-generators/create-get-swagger-json-action.d.ts +0 -14
  61. package/esm/endpoint-generators/create-get-swagger-json-action.d.ts.map +0 -1
  62. package/esm/endpoint-generators/create-get-swagger-json-action.js +0 -18
  63. package/esm/endpoint-generators/create-get-swagger-json-action.js.map +0 -1
  64. package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts +0 -21
  65. package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts.map +0 -1
  66. package/esm/endpoint-generators/with-schema-and-swagger-action.js +0 -2
  67. package/esm/endpoint-generators/with-schema-and-swagger-action.js.map +0 -1
  68. package/esm/swagger/generate-swagger-json.d.ts +0 -14
  69. package/esm/swagger/generate-swagger-json.d.ts.map +0 -1
  70. package/esm/swagger/generate-swagger-json.js +0 -108
  71. package/esm/swagger/generate-swagger-json.js.map +0 -1
  72. package/src/endpoint-generators/create-get-swagger-json-action.ts +0 -26
  73. package/src/endpoint-generators/with-schema-and-swagger-action.ts +0 -11
  74. package/src/swagger/generate-swagger-json.ts +0 -132
@@ -0,0 +1,198 @@
1
+ import type {
2
+ ApiDocumentMetadata,
3
+ ApiEndpointDefinition,
4
+ ApiEndpointSchema,
5
+ Method,
6
+ OpenApiDocument,
7
+ Operation,
8
+ ParameterObject,
9
+ RequestBodyObject,
10
+ } from '@furystack/rest'
11
+
12
+ const PATH_ITEM_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'] as const
13
+ type PathItemMethod = (typeof PATH_ITEM_METHODS)[number]
14
+
15
+ const isPathItemMethod = (method: string): method is PathItemMethod =>
16
+ PATH_ITEM_METHODS.includes(method as PathItemMethod)
17
+
18
+ type SchemaDefinition = {
19
+ properties?: Record<string, unknown>
20
+ required?: string[]
21
+ [key: string]: unknown
22
+ }
23
+
24
+ type SchemaWithDefinitions = {
25
+ definitions?: Record<string, SchemaDefinition>
26
+ [key: string]: unknown
27
+ }
28
+
29
+ const getSubSchema = (
30
+ schema: unknown,
31
+ schemaName: string,
32
+ property: string,
33
+ ): { subSchema: unknown; isRequired: boolean } | undefined => {
34
+ const typedSchema = schema as SchemaWithDefinitions | undefined
35
+ if (!typedSchema?.definitions) return undefined
36
+
37
+ const definition = typedSchema.definitions[schemaName]
38
+ if (!definition?.properties?.[property]) return undefined
39
+
40
+ return {
41
+ subSchema: definition.properties[property],
42
+ isRequired: definition.required?.includes(property) ?? false,
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Converts a FuryStack API schema to an OpenAPI 3.1 compatible document.
48
+ * Preserves metadata from ApiEndpointDefinition and ApiDocumentMetadata when available.
49
+ */
50
+ export const generateOpenApiDocument = ({
51
+ api,
52
+ title = 'FuryStack API',
53
+ description = 'API documentation generated from FuryStack API schema',
54
+ version = '1.0.0',
55
+ metadata,
56
+ }: {
57
+ api: ApiEndpointSchema['endpoints']
58
+ title?: string
59
+ description?: string
60
+ version?: string
61
+ metadata?: ApiDocumentMetadata
62
+ }): OpenApiDocument => {
63
+ const doc: OpenApiDocument = {
64
+ openapi: '3.1.0',
65
+ info: {
66
+ title,
67
+ version,
68
+ description,
69
+ ...(metadata?.summary ? { summary: metadata.summary } : {}),
70
+ ...(metadata?.termsOfService ? { termsOfService: metadata.termsOfService } : {}),
71
+ ...(metadata?.contact ? { contact: metadata.contact } : {}),
72
+ ...(metadata?.license ? { license: metadata.license } : {}),
73
+ },
74
+ jsonSchemaDialect: 'https://spec.openapis.org/oas/3.1/dialect/base',
75
+ servers: metadata?.servers ?? [{ url: '/' }],
76
+ tags: metadata?.tags ?? [],
77
+ ...(metadata?.externalDocs ? { externalDocs: metadata.externalDocs } : {}),
78
+ paths: {},
79
+ components: {
80
+ schemas: {},
81
+ securitySchemes: metadata?.securitySchemes ?? {
82
+ cookieAuth: {
83
+ type: 'apiKey',
84
+ in: 'cookie',
85
+ name: 'session',
86
+ },
87
+ },
88
+ },
89
+ }
90
+
91
+ const defaultSchemeNames = metadata?.securitySchemes ? Object.keys(metadata.securitySchemes) : ['cookieAuth']
92
+
93
+ for (const [methodKey, paths] of Object.entries(api) as Array<[Method, Record<string, ApiEndpointDefinition>]>) {
94
+ for (const [path, definition] of Object.entries(paths)) {
95
+ const normalizedPath = path.replace(/:([^/]+)/g, '{$1}')
96
+ if (!doc.paths![normalizedPath]) {
97
+ doc.paths![normalizedPath] = {}
98
+ }
99
+
100
+ const pathParams = Array.from(path.matchAll(/:([^/]+)/g), (m) => m[1])
101
+ const parameters: ParameterObject[] = pathParams.map((param) => ({
102
+ name: param,
103
+ in: 'path',
104
+ required: true,
105
+ description: `Path parameter: ${param}`,
106
+ schema: { type: 'string' },
107
+ }))
108
+
109
+ const queryInfo = getSubSchema(definition.schema, definition.schemaName, 'query')
110
+ if (queryInfo) {
111
+ const querySchema = queryInfo.subSchema as { properties?: Record<string, unknown>; required?: string[] }
112
+ if (querySchema?.properties) {
113
+ for (const [paramName, paramSchema] of Object.entries(querySchema.properties)) {
114
+ parameters.push({
115
+ name: paramName,
116
+ in: 'query',
117
+ required: querySchema.required?.includes(paramName) ?? false,
118
+ schema: paramSchema as object,
119
+ })
120
+ }
121
+ }
122
+ }
123
+
124
+ const headerInfo = getSubSchema(definition.schema, definition.schemaName, 'headers')
125
+ if (headerInfo) {
126
+ const headerSchema = headerInfo.subSchema as { properties?: Record<string, unknown>; required?: string[] }
127
+ if (headerSchema?.properties) {
128
+ for (const [headerName, headerParamSchema] of Object.entries(headerSchema.properties)) {
129
+ parameters.push({
130
+ name: headerName,
131
+ in: 'header',
132
+ required: headerSchema.required?.includes(headerName) ?? false,
133
+ schema: headerParamSchema as object,
134
+ })
135
+ }
136
+ }
137
+ }
138
+
139
+ const operationSecurity = definition.securitySchemes
140
+ ? definition.securitySchemes.map((name) => ({ [name]: [] }))
141
+ : definition.isAuthenticated
142
+ ? defaultSchemeNames.map((name) => ({ [name]: [] }))
143
+ : []
144
+
145
+ const method = methodKey.toLowerCase()
146
+ const operation: Operation = {
147
+ summary: definition.summary ?? `${methodKey} ${path}`,
148
+ description: definition.description ?? `Endpoint for ${path}`,
149
+ operationId: `${method}${path.replace(/\//g, '_').replace(/:/g, '').replace(/-/g, '_')}`,
150
+ security: operationSecurity,
151
+ parameters,
152
+ ...(definition.tags?.length ? { tags: definition.tags } : {}),
153
+ ...(definition.deprecated ? { deprecated: true } : {}),
154
+ responses: {
155
+ '200': {
156
+ description: 'Successful operation',
157
+ content: {
158
+ 'application/json': {
159
+ schema: definition.schemaName
160
+ ? { $ref: `#/components/schemas/${definition.schemaName}` }
161
+ : { type: 'object' },
162
+ },
163
+ },
164
+ },
165
+ '401': { description: 'Unauthorized' },
166
+ '500': { description: 'Internal server error' },
167
+ },
168
+ }
169
+
170
+ const bodyInfo = getSubSchema(definition.schema, definition.schemaName, 'body')
171
+ if (bodyInfo) {
172
+ const requestBody: RequestBodyObject = {
173
+ required: bodyInfo.isRequired,
174
+ content: {
175
+ 'application/json': {
176
+ schema: bodyInfo.subSchema as object,
177
+ },
178
+ },
179
+ }
180
+ operation.requestBody = requestBody
181
+ }
182
+
183
+ if (definition.schema && definition.schemaName) {
184
+ doc.components!.schemas![definition.schemaName] = definition.schema
185
+ }
186
+
187
+ const pathItem = doc.paths![normalizedPath]
188
+ if (isPathItemMethod(method)) {
189
+ pathItem[method] = operation
190
+ }
191
+ }
192
+ }
193
+
194
+ return doc
195
+ }
196
+
197
+ /** @deprecated Use generateOpenApiDocument instead */
198
+ export const generateSwaggerJsonFromApiSchema = generateOpenApiDocument
@@ -0,0 +1,363 @@
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "Advanced Pet Store API",
5
+ "version": "2.1.0",
6
+ "description": "A feature-rich API exercising all OpenAPI 3.1 constructs",
7
+ "summary": "Pet store with advanced features",
8
+ "termsOfService": "https://example.com/terms",
9
+ "contact": {
10
+ "name": "API Support",
11
+ "url": "https://example.com/support",
12
+ "email": "support@example.com"
13
+ },
14
+ "license": {
15
+ "name": "MIT",
16
+ "identifier": "MIT"
17
+ }
18
+ },
19
+ "externalDocs": {
20
+ "description": "Full documentation",
21
+ "url": "https://example.com/docs"
22
+ },
23
+ "servers": [
24
+ {
25
+ "url": "https://{environment}.example.com/api/{version}",
26
+ "description": "Main server",
27
+ "variables": {
28
+ "environment": {
29
+ "default": "production",
30
+ "enum": ["production", "staging", "development"],
31
+ "description": "Server environment"
32
+ },
33
+ "version": {
34
+ "default": "v2"
35
+ }
36
+ }
37
+ }
38
+ ],
39
+ "tags": [
40
+ { "name": "pets", "description": "Pet operations" },
41
+ {
42
+ "name": "store",
43
+ "description": "Store operations",
44
+ "externalDocs": { "url": "https://example.com/docs/store" }
45
+ }
46
+ ],
47
+ "security": [{ "bearerAuth": [] }],
48
+ "paths": {
49
+ "/pets": {
50
+ "get": {
51
+ "operationId": "listPets",
52
+ "summary": "List all pets",
53
+ "description": "Returns a paginated list of pets with optional filtering",
54
+ "tags": ["pets"],
55
+ "security": [],
56
+ "parameters": [
57
+ { "$ref": "#/components/parameters/LimitParam" },
58
+ { "$ref": "#/components/parameters/OffsetParam" },
59
+ {
60
+ "name": "status",
61
+ "in": "query",
62
+ "schema": { "type": "string", "enum": ["available", "pending", "sold"] }
63
+ },
64
+ {
65
+ "name": "tags",
66
+ "in": "query",
67
+ "schema": { "type": "array", "items": { "type": "string" } }
68
+ }
69
+ ],
70
+ "responses": {
71
+ "200": {
72
+ "description": "Successful response",
73
+ "content": {
74
+ "application/json": {
75
+ "schema": {
76
+ "type": "object",
77
+ "properties": {
78
+ "items": {
79
+ "type": "array",
80
+ "items": { "$ref": "#/components/schemas/Pet" }
81
+ },
82
+ "total": { "type": "integer" }
83
+ },
84
+ "required": ["items", "total"]
85
+ }
86
+ }
87
+ }
88
+ },
89
+ "400": { "$ref": "#/components/responses/BadRequest" }
90
+ }
91
+ },
92
+ "post": {
93
+ "operationId": "createPet",
94
+ "summary": "Create a pet",
95
+ "tags": ["pets"],
96
+ "requestBody": {
97
+ "required": true,
98
+ "description": "Pet to create",
99
+ "content": {
100
+ "application/json": {
101
+ "schema": { "$ref": "#/components/schemas/CreatePetRequest" }
102
+ }
103
+ }
104
+ },
105
+ "responses": {
106
+ "201": {
107
+ "description": "Pet created",
108
+ "content": {
109
+ "application/json": {
110
+ "schema": { "$ref": "#/components/schemas/Pet" }
111
+ }
112
+ }
113
+ },
114
+ "400": { "$ref": "#/components/responses/BadRequest" }
115
+ }
116
+ }
117
+ },
118
+ "/pets/{petId}": {
119
+ "get": {
120
+ "operationId": "getPet",
121
+ "summary": "Get a pet by ID",
122
+ "tags": ["pets"],
123
+ "parameters": [
124
+ {
125
+ "name": "petId",
126
+ "in": "path",
127
+ "required": true,
128
+ "schema": { "type": "string" }
129
+ }
130
+ ],
131
+ "responses": {
132
+ "200": {
133
+ "description": "Successful response",
134
+ "content": {
135
+ "application/json": {
136
+ "schema": { "$ref": "#/components/schemas/Pet" }
137
+ }
138
+ }
139
+ },
140
+ "404": { "$ref": "#/components/responses/NotFound" }
141
+ }
142
+ },
143
+ "patch": {
144
+ "operationId": "updatePet",
145
+ "summary": "Update a pet",
146
+ "tags": ["pets"],
147
+ "parameters": [
148
+ {
149
+ "name": "petId",
150
+ "in": "path",
151
+ "required": true,
152
+ "schema": { "type": "string" }
153
+ }
154
+ ],
155
+ "requestBody": {
156
+ "content": {
157
+ "application/json": {
158
+ "schema": {
159
+ "type": "object",
160
+ "properties": {
161
+ "name": { "type": "string" },
162
+ "status": { "type": "string", "enum": ["available", "pending", "sold"] },
163
+ "tag": { "type": ["string", "null"] }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ },
169
+ "responses": {
170
+ "200": {
171
+ "description": "Pet updated",
172
+ "content": {
173
+ "application/json": {
174
+ "schema": { "$ref": "#/components/schemas/Pet" }
175
+ }
176
+ }
177
+ }
178
+ }
179
+ },
180
+ "delete": {
181
+ "operationId": "deletePet",
182
+ "summary": "Delete a pet",
183
+ "tags": ["pets"],
184
+ "deprecated": true,
185
+ "description": "Use archive endpoint instead",
186
+ "parameters": [
187
+ {
188
+ "name": "petId",
189
+ "in": "path",
190
+ "required": true,
191
+ "schema": { "type": "string" }
192
+ }
193
+ ],
194
+ "responses": {
195
+ "204": {
196
+ "description": "Pet deleted"
197
+ }
198
+ }
199
+ }
200
+ },
201
+ "/store/inventory": {
202
+ "get": {
203
+ "operationId": "getInventory",
204
+ "summary": "Returns pet inventories",
205
+ "tags": ["store"],
206
+ "parameters": [
207
+ {
208
+ "name": "X-Request-Id",
209
+ "in": "header",
210
+ "schema": { "type": "string" }
211
+ }
212
+ ],
213
+ "responses": {
214
+ "200": {
215
+ "description": "Inventory map",
216
+ "content": {
217
+ "application/json": {
218
+ "schema": {
219
+ "type": "object",
220
+ "additionalProperties": { "type": "integer" }
221
+ }
222
+ }
223
+ }
224
+ }
225
+ }
226
+ }
227
+ },
228
+ "/store/orders": {
229
+ "post": {
230
+ "operationId": "placeOrder",
231
+ "summary": "Place an order",
232
+ "tags": ["store"],
233
+ "requestBody": {
234
+ "required": true,
235
+ "content": {
236
+ "application/json": {
237
+ "schema": { "$ref": "#/components/schemas/OrderRequest" }
238
+ }
239
+ }
240
+ },
241
+ "responses": {
242
+ "201": {
243
+ "description": "Order placed",
244
+ "content": {
245
+ "application/json": {
246
+ "schema": { "$ref": "#/components/schemas/Order" }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ }
252
+ }
253
+ },
254
+ "components": {
255
+ "schemas": {
256
+ "Pet": {
257
+ "allOf": [
258
+ { "$ref": "#/components/schemas/CreatePetRequest" },
259
+ {
260
+ "type": "object",
261
+ "properties": {
262
+ "id": { "type": "string" },
263
+ "createdAt": { "type": "string" },
264
+ "status": { "type": "string", "enum": ["available", "pending", "sold"] }
265
+ },
266
+ "required": ["id", "createdAt", "status"]
267
+ }
268
+ ]
269
+ },
270
+ "CreatePetRequest": {
271
+ "type": "object",
272
+ "properties": {
273
+ "name": { "type": "string" },
274
+ "tag": { "type": ["string", "null"] },
275
+ "category": { "$ref": "#/components/schemas/Category" }
276
+ },
277
+ "required": ["name"]
278
+ },
279
+ "Category": {
280
+ "type": "object",
281
+ "properties": {
282
+ "id": { "type": "integer" },
283
+ "name": { "type": "string" }
284
+ },
285
+ "required": ["id", "name"]
286
+ },
287
+ "Order": {
288
+ "type": "object",
289
+ "properties": {
290
+ "id": { "type": "string" },
291
+ "petId": { "type": "string" },
292
+ "quantity": { "type": "integer" },
293
+ "status": {
294
+ "oneOf": [{ "const": "placed" }, { "const": "approved" }, { "const": "delivered" }]
295
+ },
296
+ "shipDate": { "type": ["string", "null"] },
297
+ "complete": { "type": "boolean" }
298
+ },
299
+ "required": ["id", "petId", "quantity", "status", "complete"]
300
+ },
301
+ "OrderRequest": {
302
+ "type": "object",
303
+ "properties": {
304
+ "petId": { "type": "string" },
305
+ "quantity": { "type": "integer" }
306
+ },
307
+ "required": ["petId", "quantity"]
308
+ },
309
+ "Error": {
310
+ "type": "object",
311
+ "properties": {
312
+ "code": { "type": "integer" },
313
+ "message": { "type": "string" }
314
+ },
315
+ "required": ["code", "message"]
316
+ }
317
+ },
318
+ "parameters": {
319
+ "LimitParam": {
320
+ "name": "limit",
321
+ "in": "query",
322
+ "description": "Maximum number of items to return",
323
+ "schema": { "type": "integer" }
324
+ },
325
+ "OffsetParam": {
326
+ "name": "offset",
327
+ "in": "query",
328
+ "description": "Number of items to skip",
329
+ "schema": { "type": "integer" }
330
+ }
331
+ },
332
+ "responses": {
333
+ "BadRequest": {
334
+ "description": "Bad request",
335
+ "content": {
336
+ "application/json": {
337
+ "schema": { "$ref": "#/components/schemas/Error" }
338
+ }
339
+ }
340
+ },
341
+ "NotFound": {
342
+ "description": "Resource not found",
343
+ "content": {
344
+ "application/json": {
345
+ "schema": { "$ref": "#/components/schemas/Error" }
346
+ }
347
+ }
348
+ }
349
+ },
350
+ "securitySchemes": {
351
+ "bearerAuth": {
352
+ "type": "http",
353
+ "scheme": "bearer",
354
+ "bearerFormat": "JWT"
355
+ },
356
+ "apiKey": {
357
+ "type": "apiKey",
358
+ "in": "header",
359
+ "name": "X-API-Key"
360
+ }
361
+ }
362
+ }
363
+ }
@@ -0,0 +1,115 @@
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "CRUD API",
5
+ "version": "1.0.0"
6
+ },
7
+ "paths": {
8
+ "/users": {
9
+ "get": {
10
+ "operationId": "listUsers",
11
+ "responses": {
12
+ "200": {
13
+ "description": "OK",
14
+ "content": {
15
+ "application/json": {
16
+ "schema": {
17
+ "type": "array",
18
+ "items": {
19
+ "type": "object",
20
+ "properties": {
21
+ "id": { "type": "string" },
22
+ "name": { "type": "string" },
23
+ "email": { "type": "string" }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+ },
32
+ "post": {
33
+ "operationId": "createUser",
34
+ "requestBody": {
35
+ "required": true,
36
+ "content": {
37
+ "application/json": {
38
+ "schema": {
39
+ "type": "object",
40
+ "properties": {
41
+ "name": { "type": "string" },
42
+ "email": { "type": "string" }
43
+ },
44
+ "required": ["name", "email"]
45
+ }
46
+ }
47
+ }
48
+ },
49
+ "responses": {
50
+ "201": {
51
+ "description": "Created",
52
+ "content": {
53
+ "application/json": {
54
+ "schema": {
55
+ "type": "object",
56
+ "properties": {
57
+ "id": { "type": "string" },
58
+ "name": { "type": "string" },
59
+ "email": { "type": "string" }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+ },
68
+ "/users/{id}": {
69
+ "get": {
70
+ "operationId": "getUser",
71
+ "parameters": [
72
+ {
73
+ "name": "id",
74
+ "in": "path",
75
+ "required": true,
76
+ "schema": { "type": "string" }
77
+ }
78
+ ],
79
+ "responses": {
80
+ "200": {
81
+ "description": "OK",
82
+ "content": {
83
+ "application/json": {
84
+ "schema": {
85
+ "type": "object",
86
+ "properties": {
87
+ "id": { "type": "string" },
88
+ "name": { "type": "string" },
89
+ "email": { "type": "string" }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ },
97
+ "delete": {
98
+ "operationId": "deleteUser",
99
+ "parameters": [
100
+ {
101
+ "name": "id",
102
+ "in": "path",
103
+ "required": true,
104
+ "schema": { "type": "string" }
105
+ }
106
+ ],
107
+ "responses": {
108
+ "200": {
109
+ "description": "Deleted"
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ }