@furystack/rest-service 12.2.1 → 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.
- package/CHANGELOG.md +38 -0
- package/esm/api-manager.d.ts +1 -1
- package/esm/api-manager.d.ts.map +1 -1
- package/esm/api-manager.js +3 -2
- package/esm/api-manager.js.map +1 -1
- package/esm/endpoint-generators/create-get-openapi-document-action.d.ts +29 -0
- package/esm/endpoint-generators/create-get-openapi-document-action.d.ts.map +1 -0
- package/esm/endpoint-generators/create-get-openapi-document-action.js +61 -0
- package/esm/endpoint-generators/create-get-openapi-document-action.js.map +1 -0
- package/esm/endpoint-generators/index.d.ts +2 -0
- package/esm/endpoint-generators/index.d.ts.map +1 -1
- package/esm/endpoint-generators/index.js +2 -0
- package/esm/endpoint-generators/index.js.map +1 -1
- package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts +23 -0
- package/esm/endpoint-generators/with-schema-and-openapi-action.d.ts.map +1 -0
- package/esm/endpoint-generators/with-schema-and-openapi-action.js +2 -0
- package/esm/endpoint-generators/with-schema-and-openapi-action.js.map +1 -0
- package/esm/openapi/auth-provider-to-security-scheme.d.ts +14 -0
- package/esm/openapi/auth-provider-to-security-scheme.d.ts.map +1 -0
- package/esm/openapi/auth-provider-to-security-scheme.js +35 -0
- package/esm/openapi/auth-provider-to-security-scheme.js.map +1 -0
- package/esm/openapi/auth-provider-to-security-scheme.spec.d.ts +2 -0
- package/esm/openapi/auth-provider-to-security-scheme.spec.d.ts.map +1 -0
- package/esm/openapi/auth-provider-to-security-scheme.spec.js +42 -0
- package/esm/openapi/auth-provider-to-security-scheme.spec.js.map +1 -0
- package/esm/openapi/generate-openapi-document.d.ts +21 -0
- package/esm/openapi/generate-openapi-document.d.ts.map +1 -0
- package/esm/openapi/generate-openapi-document.js +144 -0
- package/esm/openapi/generate-openapi-document.js.map +1 -0
- package/esm/openapi/generate-openapi-document.spec.d.ts +2 -0
- package/esm/openapi/generate-openapi-document.spec.d.ts.map +1 -0
- package/esm/openapi/generate-openapi-document.spec.js +643 -0
- package/esm/openapi/generate-openapi-document.spec.js.map +1 -0
- package/esm/openapi/openapi-round-trip.advanced-api.json +363 -0
- package/esm/openapi/openapi-round-trip.crud-api.json +115 -0
- package/esm/openapi/openapi-round-trip.example-api.json +71 -0
- package/esm/openapi/openapi-round-trip.spec.d.ts +2 -0
- package/esm/openapi/openapi-round-trip.spec.d.ts.map +1 -0
- package/esm/openapi/openapi-round-trip.spec.js +525 -0
- package/esm/openapi/openapi-round-trip.spec.js.map +1 -0
- package/esm/swagger/generate-swagger-json.spec.js +1 -1
- package/esm/swagger/generate-swagger-json.spec.js.map +1 -1
- package/esm/validate.integration.spec.js +153 -32
- package/esm/validate.integration.spec.js.map +1 -1
- package/package.json +7 -7
- package/src/api-manager.ts +7 -3
- package/src/endpoint-generators/create-get-openapi-document-action.ts +96 -0
- package/src/endpoint-generators/index.ts +2 -0
- package/src/endpoint-generators/with-schema-and-openapi-action.ts +14 -0
- package/src/openapi/auth-provider-to-security-scheme.spec.ts +50 -0
- package/src/openapi/auth-provider-to-security-scheme.ts +41 -0
- package/src/openapi/generate-openapi-document.spec.ts +733 -0
- package/src/openapi/generate-openapi-document.ts +198 -0
- package/src/openapi/openapi-round-trip.advanced-api.json +363 -0
- package/src/openapi/openapi-round-trip.crud-api.json +115 -0
- package/src/openapi/openapi-round-trip.example-api.json +71 -0
- package/src/openapi/openapi-round-trip.spec.ts +621 -0
- package/src/swagger/generate-swagger-json.spec.ts +1 -1
- package/src/validate.integration.spec.ts +184 -33
- package/esm/endpoint-generators/create-get-swagger-json-action.d.ts +0 -14
- package/esm/endpoint-generators/create-get-swagger-json-action.d.ts.map +0 -1
- package/esm/endpoint-generators/create-get-swagger-json-action.js +0 -18
- package/esm/endpoint-generators/create-get-swagger-json-action.js.map +0 -1
- package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts +0 -21
- package/esm/endpoint-generators/with-schema-and-swagger-action.d.ts.map +0 -1
- package/esm/endpoint-generators/with-schema-and-swagger-action.js +0 -2
- package/esm/endpoint-generators/with-schema-and-swagger-action.js.map +0 -1
- package/esm/swagger/generate-swagger-json.d.ts +0 -14
- package/esm/swagger/generate-swagger-json.d.ts.map +0 -1
- package/esm/swagger/generate-swagger-json.js +0 -108
- package/esm/swagger/generate-swagger-json.js.map +0 -1
- package/src/endpoint-generators/create-get-swagger-json-action.ts +0 -26
- package/src/endpoint-generators/with-schema-and-swagger-action.ts +0 -11
- 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
|
+
}
|