@grafana/openapi-to-k6 0.2.6 → 0.3.1

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 (124) hide show
  1. package/README.md +5 -1
  2. package/dist/constants.js +7 -3
  3. package/dist/generator/index.js +13 -6
  4. package/dist/generator/k6Client.js +8 -56
  5. package/dist/generator/k6ScriptBuilder.js +256 -0
  6. package/dist/helper.js +16 -0
  7. package/examples/basic_schema/schema.json +2 -1
  8. package/examples/basic_schema/single/k6-script.sample.ts +3 -2
  9. package/examples/basic_schema/single/simpleAPI.ts +4 -1
  10. package/examples/basic_schema/split/k6-script.sample.ts +3 -2
  11. package/examples/basic_schema/split/simpleAPI.schemas.ts +2 -1
  12. package/examples/basic_schema/split/simpleAPI.ts +3 -1
  13. package/examples/basic_schema/tags/default.ts +3 -1
  14. package/examples/basic_schema/tags/k6-script.sample.ts +4 -3
  15. package/examples/basic_schema/tags/simpleAPI.schemas.ts +2 -1
  16. package/examples/form_data_schema/schema.json +6 -3
  17. package/examples/form_data_schema/single/formDataAPI.ts +8 -6
  18. package/examples/form_data_schema/single/k6-script.sample.ts +10 -2
  19. package/examples/form_data_schema/split/formDataAPI.schemas.ts +5 -5
  20. package/examples/form_data_schema/split/formDataAPI.ts +4 -2
  21. package/examples/form_data_schema/split/k6-script.sample.ts +10 -2
  22. package/examples/form_data_schema/tags/default.ts +4 -2
  23. package/examples/form_data_schema/tags/formDataAPI.schemas.ts +5 -5
  24. package/examples/form_data_schema/tags/k6-script.sample.ts +11 -3
  25. package/examples/form_url_encoded_data_schema/schema.json +6 -3
  26. package/examples/form_url_encoded_data_schema/single/formURLEncodedAPI.ts +8 -6
  27. package/examples/form_url_encoded_data_schema/single/k6-script.sample.ts +11 -2
  28. package/examples/form_url_encoded_data_schema/split/formURLEncodedAPI.schemas.ts +5 -5
  29. package/examples/form_url_encoded_data_schema/split/formURLEncodedAPI.ts +4 -2
  30. package/examples/form_url_encoded_data_schema/split/k6-script.sample.ts +11 -2
  31. package/examples/form_url_encoded_data_schema/tags/default.ts +4 -2
  32. package/examples/form_url_encoded_data_schema/tags/formURLEncodedAPI.schemas.ts +5 -5
  33. package/examples/form_url_encoded_data_schema/tags/k6-script.sample.ts +12 -3
  34. package/examples/form_url_encoded_data_with_query_params_schema/schema.json +8 -4
  35. package/examples/form_url_encoded_data_with_query_params_schema/single/formURLEncodedAPIWithQueryParameters.ts +8 -6
  36. package/examples/form_url_encoded_data_with_query_params_schema/single/k6-script.sample.ts +19 -6
  37. package/examples/form_url_encoded_data_with_query_params_schema/split/formURLEncodedAPIWithQueryParameters.schemas.ts +5 -5
  38. package/examples/form_url_encoded_data_with_query_params_schema/split/formURLEncodedAPIWithQueryParameters.ts +4 -2
  39. package/examples/form_url_encoded_data_with_query_params_schema/split/k6-script.sample.ts +19 -6
  40. package/examples/form_url_encoded_data_with_query_params_schema/tags/default.ts +4 -2
  41. package/examples/form_url_encoded_data_with_query_params_schema/tags/formURLEncodedAPIWithQueryParameters.schemas.ts +5 -5
  42. package/examples/form_url_encoded_data_with_query_params_schema/tags/k6-script.sample.ts +14 -3
  43. package/examples/get_request_with_path_parameters_schema/schema.json +2 -1
  44. package/examples/get_request_with_path_parameters_schema/single/k6-script.sample.ts +6 -2
  45. package/examples/get_request_with_path_parameters_schema/single/simpleAPI.ts +4 -2
  46. package/examples/get_request_with_path_parameters_schema/split/k6-script.sample.ts +6 -2
  47. package/examples/get_request_with_path_parameters_schema/split/simpleAPI.schemas.ts +2 -2
  48. package/examples/get_request_with_path_parameters_schema/split/simpleAPI.ts +3 -1
  49. package/examples/get_request_with_path_parameters_schema/tags/default.ts +3 -1
  50. package/examples/get_request_with_path_parameters_schema/tags/k6-script.sample.ts +7 -3
  51. package/examples/get_request_with_path_parameters_schema/tags/simpleAPI.schemas.ts +2 -2
  52. package/examples/headers_schema/schema.json +2 -1
  53. package/examples/headers_schema/single/headerDemoAPI.ts +5 -3
  54. package/examples/headers_schema/single/k6-script.sample.ts +15 -4
  55. package/examples/headers_schema/split/headerDemoAPI.schemas.ts +1 -1
  56. package/examples/headers_schema/split/headerDemoAPI.ts +5 -3
  57. package/examples/headers_schema/split/k6-script.sample.ts +15 -4
  58. package/examples/headers_schema/tags/default.ts +5 -3
  59. package/examples/headers_schema/tags/headerDemoAPI.schemas.ts +1 -1
  60. package/examples/headers_schema/tags/k6-script.sample.ts +16 -5
  61. package/examples/no_title_schema/single/k6-script.sample.ts +3 -2
  62. package/examples/no_title_schema/single/k6Client.ts +3 -1
  63. package/examples/no_title_schema/split/k6-script.sample.ts +3 -2
  64. package/examples/no_title_schema/split/k6Client.schemas.ts +1 -1
  65. package/examples/no_title_schema/split/k6Client.ts +3 -1
  66. package/examples/no_title_schema/tags/default.ts +3 -1
  67. package/examples/no_title_schema/tags/k6-script.sample.ts +4 -3
  68. package/examples/no_title_schema/tags/k6Client.schemas.ts +1 -1
  69. package/examples/post_request_with_query_params/schema.json +14 -7
  70. package/examples/post_request_with_query_params/single/exampleAPI.ts +8 -6
  71. package/examples/post_request_with_query_params/single/k6-script.sample.ts +11 -2
  72. package/examples/post_request_with_query_params/split/exampleAPI.schemas.ts +5 -5
  73. package/examples/post_request_with_query_params/split/exampleAPI.ts +4 -2
  74. package/examples/post_request_with_query_params/split/k6-script.sample.ts +11 -2
  75. package/examples/post_request_with_query_params/tags/default.ts +4 -2
  76. package/examples/post_request_with_query_params/tags/exampleAPI.schemas.ts +5 -5
  77. package/examples/post_request_with_query_params/tags/k6-script.sample.ts +12 -3
  78. package/examples/query_params_schema/schema.json +20 -10
  79. package/examples/query_params_schema/single/exampleAPI.ts +5 -3
  80. package/examples/query_params_schema/single/k6-script.sample.ts +8 -2
  81. package/examples/query_params_schema/split/exampleAPI.schemas.ts +3 -3
  82. package/examples/query_params_schema/split/exampleAPI.ts +3 -1
  83. package/examples/query_params_schema/split/k6-script.sample.ts +8 -2
  84. package/examples/query_params_schema/tags/default.ts +3 -1
  85. package/examples/query_params_schema/tags/exampleAPI.schemas.ts +3 -3
  86. package/examples/query_params_schema/tags/k6-script.sample.ts +9 -3
  87. package/examples/simple_post_request_schema/schema.json +30 -15
  88. package/examples/simple_post_request_schema/single/exampleAPI.ts +14 -12
  89. package/examples/simple_post_request_schema/single/k6-script.sample.ts +16 -2
  90. package/examples/simple_post_request_schema/split/exampleAPI.schemas.ts +11 -11
  91. package/examples/simple_post_request_schema/split/exampleAPI.ts +4 -2
  92. package/examples/simple_post_request_schema/split/k6-script.sample.ts +16 -2
  93. package/examples/simple_post_request_schema/tags/default.ts +4 -2
  94. package/examples/simple_post_request_schema/tags/exampleAPI.schemas.ts +11 -11
  95. package/examples/simple_post_request_schema/tags/k6-script.sample.ts +17 -3
  96. package/package.json +5 -2
  97. package/src/constants.ts +7 -3
  98. package/src/generator/index.ts +20 -7
  99. package/src/generator/k6Client.ts +6 -73
  100. package/src/generator/k6ScriptBuilder.ts +328 -0
  101. package/src/helper.ts +17 -0
  102. package/tests/e2e/schema.json +135 -18
  103. package/tests/e2e/single/k6Script.ts +54 -1
  104. package/tests/functional-tests/helper.ts +16 -0
  105. package/tests/functional-tests/test-generator/fixtures/basic_parameter_in_ref.json +59 -0
  106. package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/form_data_schema.json +1 -1
  107. package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/form_url_encoded_data_schema.json +1 -1
  108. package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/headers_schema.json +2 -2
  109. package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/simple_post_request_schema.json +1 -1
  110. package/tests/functional-tests/test-generator/generator.test.ts +154 -0
  111. package/tests/functional-tests/test-sample-k6-scripts/fixtures/schema_using_ref_models.json +394 -0
  112. package/tests/functional-tests/test-sample-k6-scripts/fixtures/schema_with_examples.json +416 -0
  113. package/tests/functional-tests/test-sample-k6-scripts/fixtures/schema_with_no_variables.json +32 -0
  114. package/tests/functional-tests/test-sample-k6-scripts/sampleK6Scripts.test.ts +248 -0
  115. package/tests/functional-tests/test-tags-filtering/tagsFiltering.test.ts +166 -0
  116. package/{vite.config.js → vite.config.mjs} +4 -0
  117. package/tests/functional-tests/generator.test.ts +0 -319
  118. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/basic_schema.json +0 -0
  119. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/form_url_encoded_data_with_query_params_schema.json +0 -0
  120. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/get_request_with_path_parameters_schema.json +0 -0
  121. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/no_title_schema.json +0 -0
  122. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/post_request_with_query_params.json +0 -0
  123. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/query_params_schema.json +0 -0
  124. /package/tests/functional-tests/{fixtures → test-tags-filtering/fixtures}/tags_filtering.json +0 -0
@@ -0,0 +1,59 @@
1
+ {
2
+ "openapi_schema": {
3
+ "openapi": "3.0.0",
4
+ "info": {
5
+ "title": "Simple API",
6
+ "version": "1.0.0"
7
+ },
8
+ "paths": {
9
+ "/example": {
10
+ "get": {
11
+ "summary": "Retrieve example data",
12
+ "parameters": [
13
+ {
14
+ "$ref": "#/components/parameters/stackId"
15
+ }
16
+ ],
17
+ "responses": {
18
+ "200": {
19
+ "description": "Successful response",
20
+ "content": {
21
+ "application/json": {
22
+ "schema": {
23
+ "type": "object",
24
+ "properties": {
25
+ "message": {
26
+ "type": "string",
27
+ "example": "Hello, World!"
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ },
38
+ "components": {
39
+ "parameters": {
40
+ "stackId": {
41
+ "in": "header",
42
+ "name": "X-Stack-Id",
43
+ "schema": {
44
+ "type": "integer"
45
+ },
46
+ "required": true
47
+ }
48
+ }
49
+ }
50
+ },
51
+ "expected_sdk": {
52
+ "fileName": "simpleAPI.ts",
53
+ "expectedSubstrings": [
54
+ "export class SimpleAPIClient",
55
+ "export type StackIdParameter = number;",
56
+ "export type GetExampleHeaders = { \"X-Stack-Id\": StackIdParameter; };"
57
+ ]
58
+ }
59
+ }
@@ -88,7 +88,7 @@
88
88
  "export class FormDataAPIClient",
89
89
  "const formData = new FormData(); formData.append(\"file\", postUploadBody.file); if (postUploadBody.description !== undefined) { formData.append(\"description\", postUploadBody.description); } formData.append(\"userId\", postUploadBody.userId);",
90
90
  "const url = new URL(this.cleanBaseUrl + `/upload`);",
91
- "const response = http.request(\"POST\", url.toString(), formData.body(), { ...mergedRequestParameters, headers: { \"Content-Type\": \"multipart/form-data; boundary=\" + formData.boundary, ...mergedRequestParameters?.headers, }, });"
91
+ "const response = http.request(\"POST\", url.toString(), formData.body(), { ...mergedRequestParameters, headers: { ...mergedRequestParameters?.headers, \"Content-Type\": \"multipart/form-data; boundary=\" + formData.boundary, }, });"
92
92
  ]
93
93
  }
94
94
  }
@@ -87,7 +87,7 @@
87
87
  "expectedSubstrings": [
88
88
  "export class FormURLEncodedAPIClient",
89
89
  "const url = new URL(this.cleanBaseUrl + `/submit-form`);",
90
- "const response = http.request( \"POST\", url.toString(), JSON.stringify(postSubmitFormBody), { ...mergedRequestParameters, headers: { \"Content-Type\": \"application/x-www-form-urlencoded\", ...mergedRequestParameters?.headers, }, }, );"
90
+ "const response = http.request( \"POST\", url.toString(), JSON.stringify(postSubmitFormBody), { ...mergedRequestParameters, headers: { ...mergedRequestParameters?.headers, \"Content-Type\": \"application/x-www-form-urlencoded\", }, }, );"
91
91
  ]
92
92
  }
93
93
  }
@@ -152,9 +152,9 @@
152
152
  "expectedSubstrings": [
153
153
  "export class HeaderDemoAPIClient",
154
154
  "getExampleGet( headers?: GetExampleGetHeaders, requestParameters?: Params, ): { response: Response; data: GetExampleGet200; }",
155
- "response = http.request(\"GET\", url.toString(), undefined, { ...mergedRequestParameters, headers: { // In the schema, headers can be of any type like number but k6 accepts only strings as headers, hence converting all headers to string ...Object.fromEntries( Object.entries(headers || {}).map(([key, value]) => [ key, String(value), ]), ), ...mergedRequestParameters?.headers, }, });",
155
+ "response = http.request(\"GET\", url.toString(), undefined, { ...mergedRequestParameters, headers: { ...mergedRequestParameters?.headers, // In the schema, headers can be of any type like number but k6 accepts only strings as headers, hence converting all headers to string ...Object.fromEntries( Object.entries(headers || {}).map(([key, value]) => [ key, String(value), ]), ), }, });",
156
156
  "postExamplePost( postExamplePostBody: PostExamplePostBody, headers: PostExamplePostHeaders, requestParameters?: Params, ): { response: Response; data: void; }",
157
- "response = http.request( \"POST\", url.toString(), JSON.stringify(postExamplePostBody), { ...mergedRequestParameters, headers: { \"Content-Type\": \"application/json\", // In the schema, headers can be of any type like number but k6 accepts only strings as headers, hence converting all headers to string ...Object.fromEntries( Object.entries(headers || {}).map(([key, value]) => [ key, String(value), ]), ), ...mergedRequestParameters?.headers, }, }, );",
157
+ "response = http.request( \"POST\", url.toString(), JSON.stringify(postExamplePostBody), { ...mergedRequestParameters, headers: { ...mergedRequestParameters?.headers, \"Content-Type\": \"application/json\", // In the schema, headers can be of any type like number but k6 accepts only strings as headers, hence converting all headers to string ...Object.fromEntries( Object.entries(headers || {}).map(([key, value]) => [ key, String(value), ]), ), }, }, );",
158
158
  "getExampleResponseHeaders(requestParameters?: Params): { response: Response; data: GetExampleResponseHeaders200; }",
159
159
  "response = http.request( \"GET\", url.toString(), undefined, mergedRequestParameters, );"
160
160
  ]
@@ -124,7 +124,7 @@
124
124
  "const url = new URL(this.cleanBaseUrl + `/example`);",
125
125
  "JSON.stringify(createExampleDataBody),",
126
126
  "POST",
127
- "export type CreateExampleData201 = { age?: number; date?: string; /** The unique ID of the created resource */ id?: string; isActive?: boolean; meta?: CreateExampleData201Meta; name?: string; tags?: string[];"
127
+ "export type CreateExampleData201 = { /** The unique ID of the created resource */ id?: string; name?: string; age?: number; isActive?: boolean; tags?: string[]; date?: string; meta?: CreateExampleData201Meta; };"
128
128
  ]
129
129
  }
130
130
  }
@@ -0,0 +1,154 @@
1
+ import fs from 'fs'
2
+ import os from 'os'
3
+ import path from 'path'
4
+ import {
5
+ afterAll,
6
+ afterEach,
7
+ beforeAll,
8
+ beforeEach,
9
+ describe,
10
+ expect,
11
+ it,
12
+ } from 'vitest'
13
+ import { Mode } from '../../../src/constants'
14
+ import generator from '../../../src/generator'
15
+
16
+ import {
17
+ loadFixture,
18
+ mkdtemp,
19
+ readFile,
20
+ replaceSpacesAndNewLineToSingleSpace,
21
+ rmdir,
22
+ writeFile,
23
+ } from '../helper'
24
+
25
+ const commonSubstringsForAllSDK = [
26
+ 'Automatically generated by',
27
+ 'Do not edit manually',
28
+ 'Service version',
29
+ 'const mergedRequestParameters = this._mergeRequestParameters( requestParameters || {}, this.commonRequestParameters, );',
30
+ 'try { data = response.json(); } catch { data = response.body; }',
31
+ 'return { response, data, };',
32
+ ]
33
+
34
+ const commonSubstringsForK6SampleScript = [`const baseUrl = "<BASE_URL>";`]
35
+
36
+ describe('generator', () => {
37
+ let tempDir: string, schemaDirectory: string
38
+ const allFixtures = fs.readdirSync(path.join(__dirname, 'fixtures'))
39
+
40
+ beforeAll(async () => {
41
+ tempDir = await mkdtemp(path.join(os.tmpdir(), 'sdk-generator-'))
42
+ })
43
+
44
+ afterAll(async () => {
45
+ await rmdir(tempDir, { recursive: true })
46
+ })
47
+
48
+ for (const fixtureName of allFixtures) {
49
+ describe(`test ${fixtureName} OpenAPI schema`, () => {
50
+ let openApiPath: string, generatedSchemaPath: string
51
+ const fixture = loadFixture(path.join(__dirname, 'fixtures', fixtureName))
52
+
53
+ beforeEach(async () => {
54
+ schemaDirectory = await mkdtemp(
55
+ path.join(tempDir, fixtureName.replace('.', '-'))
56
+ )
57
+ // Write the OpenAPI schema to a file
58
+
59
+ openApiPath = path.join(schemaDirectory, 'openapi-schema.json')
60
+
61
+ await writeFile(openApiPath, JSON.stringify(fixture['openapi_schema']))
62
+
63
+ generatedSchemaPath = path.join(schemaDirectory, 'generated-schema')
64
+ })
65
+
66
+ afterEach(async () => {
67
+ await rmdir(schemaDirectory, { recursive: true })
68
+ })
69
+
70
+ it(`should generate SDK client from the OpenAPI schema`, async () => {
71
+ const expectedGeneratedCode = fixture['expected_sdk']
72
+
73
+ await generator({
74
+ openApiPath,
75
+ outputDir: generatedSchemaPath,
76
+ mode: Mode.SINGLE,
77
+ })
78
+
79
+ const generatedFiles = fs.readdirSync(generatedSchemaPath)
80
+ expect(generatedFiles.length).toBe(1)
81
+ expect(generatedFiles[0]).toBeDefined()
82
+ const fileName = path.basename(generatedFiles[0]!)
83
+ expect(fileName).toBe(expectedGeneratedCode.fileName)
84
+ const generatedFilePath = path.join(
85
+ generatedSchemaPath,
86
+ generatedFiles[0]!
87
+ )
88
+ const generatedContent = await readFile(generatedFilePath, 'utf-8')
89
+
90
+ for (const expectedString of [
91
+ ...expectedGeneratedCode['expectedSubstrings'],
92
+ ...commonSubstringsForAllSDK,
93
+ ]) {
94
+ expect(
95
+ replaceSpacesAndNewLineToSingleSpace(generatedContent)
96
+ ).toContain(expectedString)
97
+ }
98
+ })
99
+
100
+ it('should generate a sample K6 script', async () => {
101
+ await generator({
102
+ openApiPath,
103
+ outputDir: generatedSchemaPath,
104
+ shouldGenerateSampleK6Script: true,
105
+ mode: Mode.SINGLE,
106
+ })
107
+
108
+ const generatedFiles = fs.readdirSync(generatedSchemaPath)
109
+ expect(generatedFiles.length).toBe(2)
110
+ const k6ScriptFile = generatedFiles.find((file) =>
111
+ file.includes('k6-script')
112
+ )
113
+ expect(k6ScriptFile).toBeDefined()
114
+ expect(k6ScriptFile).toBe('k6-script.sample.ts')
115
+
116
+ const generatedFilePath = path.join(generatedSchemaPath, k6ScriptFile!)
117
+ const generatedContent = await readFile(generatedFilePath, 'utf-8')
118
+
119
+ for (const expectedString of [...commonSubstringsForK6SampleScript]) {
120
+ expect(generatedContent).toContain(expectedString)
121
+ }
122
+ })
123
+
124
+ it('should not contain types in main file when using split mode', async () => {
125
+ await generator({
126
+ openApiPath,
127
+ outputDir: generatedSchemaPath,
128
+ shouldGenerateSampleK6Script: true,
129
+ mode: Mode.SPLIT,
130
+ })
131
+ const expectedGeneratedCode = fixture['expected_sdk']
132
+ const generatedFiles = fs.readdirSync(generatedSchemaPath)
133
+
134
+ expect(generatedFiles.length).toBe(3)
135
+
136
+ const clientFile = generatedFiles.find((file) =>
137
+ file.includes(expectedGeneratedCode.fileName)
138
+ )
139
+ const schemaFile = generatedFiles.find((file) =>
140
+ file.includes('.schemas.ts')
141
+ )
142
+
143
+ expect(clientFile).toBeDefined()
144
+ expect(schemaFile).toBeDefined()
145
+
146
+ const generatedFilePath = path.join(generatedSchemaPath, clientFile!)
147
+ const generatedContent = await readFile(generatedFilePath, 'utf-8')
148
+
149
+ expect(generatedContent).not.toContain('export type')
150
+ expect(generatedContent).not.toContain('export interface')
151
+ })
152
+ })
153
+ }
154
+ })
@@ -0,0 +1,394 @@
1
+ {
2
+ "openapi": "3.0.3",
3
+ "info": {
4
+ "title": "Comprehensive API",
5
+ "version": "1.0.0",
6
+ "description": "API supporting various HTTP methods with query, path parameters, and headers"
7
+ },
8
+ "components": {
9
+ "schemas": {
10
+ "ItemBase": {
11
+ "type": "object",
12
+ "properties": {
13
+ "name": {
14
+ "type": "string",
15
+ "example": "Sample Item"
16
+ },
17
+ "description": {
18
+ "type": "string",
19
+ "example": "This is a sample description for the item."
20
+ }
21
+ }
22
+ },
23
+ "ItemId": {
24
+ "type": "string",
25
+ "example": "12345-getItemById"
26
+ },
27
+ "UpdatedItem": {
28
+ "type": "object",
29
+ "properties": {
30
+ "name": {
31
+ "type": "string",
32
+ "example": "Updated Item Name"
33
+ },
34
+ "description": {
35
+ "type": "string",
36
+ "example": "Updated description for the item."
37
+ }
38
+ }
39
+ },
40
+ "PatchItem": {
41
+ "type": "object",
42
+ "properties": {
43
+ "name": {
44
+ "type": "string",
45
+ "example": "Partially Updated Item Name"
46
+ }
47
+ }
48
+ },
49
+ "FormUrlEncodedItem": {
50
+ "type": "object",
51
+ "properties": {
52
+ "name": {
53
+ "type": "string",
54
+ "example": "Form Encoded Item"
55
+ },
56
+ "description": {
57
+ "type": "string",
58
+ "example": "Description for form-urlencoded item."
59
+ }
60
+ },
61
+ "required": ["name"]
62
+ },
63
+ "FormDataItem": {
64
+ "type": "object",
65
+ "properties": {
66
+ "name": {
67
+ "type": "string",
68
+ "example": "Form Data Item"
69
+ }
70
+ },
71
+ "required": ["file", "name"]
72
+ },
73
+ "UserPost": {
74
+ "type": "object",
75
+ "properties": {
76
+ "title": {
77
+ "type": "string"
78
+ },
79
+ "content": {
80
+ "type": "string"
81
+ }
82
+ }
83
+ }
84
+ },
85
+ "parameters": {
86
+ "itemIdPath": {
87
+ "name": "id",
88
+ "in": "path",
89
+ "required": true,
90
+ "description": "ID of the item to retrieve",
91
+ "schema": {
92
+ "$ref": "#/components/schemas/ItemId"
93
+ }
94
+ },
95
+ "detailQuery": {
96
+ "name": "detail",
97
+ "in": "query",
98
+ "required": false,
99
+ "description": "Whether to return detailed information",
100
+ "schema": {
101
+ "type": "boolean",
102
+ "default": false,
103
+ "example": true
104
+ }
105
+ },
106
+ "userId": {
107
+ "name": "userId",
108
+ "in": "path",
109
+ "required": true,
110
+ "schema": {
111
+ "type": "string"
112
+ }
113
+ },
114
+ "postId": {
115
+ "name": "postId",
116
+ "in": "path",
117
+ "required": true,
118
+ "schema": {
119
+ "type": "string"
120
+ }
121
+ },
122
+ "clientIdHeader": {
123
+ "name": "X-Client-ID",
124
+ "in": "header",
125
+ "description": "Client ID for the request",
126
+ "required": true,
127
+ "schema": {
128
+ "type": "string",
129
+ "example": "client-123"
130
+ }
131
+ },
132
+ "requestIdHeader": {
133
+ "name": "X-Request-ID",
134
+ "in": "header",
135
+ "description": "Unique request identifier",
136
+ "required": false,
137
+ "schema": {
138
+ "type": "string",
139
+ "example": "req-56789"
140
+ }
141
+ },
142
+ "authTokenHeader": {
143
+ "name": "X-Auth-Token",
144
+ "in": "header",
145
+ "description": "Authentication token",
146
+ "required": true,
147
+ "schema": {
148
+ "type": "string",
149
+ "example": "Bearer abcdef12345"
150
+ }
151
+ },
152
+ "correlationIdHeader": {
153
+ "name": "X-Correlation-ID",
154
+ "in": "header",
155
+ "description": "Correlation ID for tracking requests",
156
+ "required": true,
157
+ "schema": {
158
+ "type": "string",
159
+ "example": "correlation-12345"
160
+ }
161
+ },
162
+ "optionalHeader": {
163
+ "name": "X-Optional-Header",
164
+ "in": "header",
165
+ "description": "Optional header",
166
+ "required": false,
167
+ "schema": {
168
+ "type": "string",
169
+ "example": "optional-header-value"
170
+ }
171
+ }
172
+ }
173
+ },
174
+ "paths": {
175
+ "/items/{id}": {
176
+ "get": {
177
+ "tags": ["Items"],
178
+ "summary": "Get an item by ID",
179
+ "parameters": [
180
+ { "$ref": "#/components/parameters/itemIdPath" },
181
+ { "$ref": "#/components/parameters/detailQuery" }
182
+ ],
183
+ "responses": {
184
+ "200": {
185
+ "description": "Successful response"
186
+ }
187
+ }
188
+ },
189
+ "post": {
190
+ "tags": ["Items"],
191
+ "summary": "Create an item by ID",
192
+ "parameters": [{ "$ref": "#/components/parameters/itemIdPath" }],
193
+ "requestBody": {
194
+ "required": true,
195
+ "content": {
196
+ "application/json": {
197
+ "schema": {
198
+ "$ref": "#/components/schemas/ItemBase"
199
+ }
200
+ }
201
+ }
202
+ },
203
+ "responses": {
204
+ "201": {
205
+ "description": "Item created successfully"
206
+ }
207
+ }
208
+ },
209
+ "put": {
210
+ "tags": ["Items"],
211
+ "summary": "Update an item by ID",
212
+ "parameters": [{ "$ref": "#/components/parameters/itemIdPath" }],
213
+ "requestBody": {
214
+ "required": true,
215
+ "content": {
216
+ "application/json": {
217
+ "schema": {
218
+ "$ref": "#/components/schemas/UpdatedItem"
219
+ }
220
+ }
221
+ }
222
+ },
223
+ "responses": {
224
+ "200": {
225
+ "description": "Item updated successfully"
226
+ }
227
+ }
228
+ },
229
+ "patch": {
230
+ "tags": ["Items"],
231
+ "summary": "Partially update an item by ID",
232
+ "parameters": [{ "$ref": "#/components/parameters/itemIdPath" }],
233
+ "requestBody": {
234
+ "required": true,
235
+ "content": {
236
+ "application/json": {
237
+ "schema": {
238
+ "$ref": "#/components/schemas/PatchItem"
239
+ }
240
+ }
241
+ }
242
+ },
243
+ "responses": {
244
+ "200": {
245
+ "description": "Item partially updated"
246
+ }
247
+ }
248
+ },
249
+ "delete": {
250
+ "tags": ["Items"],
251
+ "summary": "Delete an item by ID",
252
+ "parameters": [{ "$ref": "#/components/parameters/itemIdPath" }],
253
+ "responses": {
254
+ "204": {
255
+ "description": "Item deleted successfully"
256
+ }
257
+ }
258
+ },
259
+ "head": {
260
+ "tags": ["Items"],
261
+ "summary": "Check if item exists",
262
+ "parameters": [{ "$ref": "#/components/parameters/itemIdPath" }],
263
+ "responses": {
264
+ "200": {
265
+ "description": "Item exists"
266
+ },
267
+ "404": {
268
+ "description": "Item not found"
269
+ }
270
+ }
271
+ }
272
+ },
273
+ "/items-form-url-encoded": {
274
+ "post": {
275
+ "tags": ["ItemsForm", "Items"],
276
+ "summary": "Create an item using form-urlencoded data",
277
+ "requestBody": {
278
+ "required": true,
279
+ "content": {
280
+ "application/x-www-form-urlencoded": {
281
+ "schema": {
282
+ "$ref": "#/components/schemas/FormUrlEncodedItem"
283
+ }
284
+ }
285
+ }
286
+ },
287
+ "responses": {
288
+ "201": {
289
+ "description": "Item created successfully"
290
+ }
291
+ }
292
+ }
293
+ },
294
+ "/items-form-data": {
295
+ "post": {
296
+ "tags": ["ItemsForm", "Items"],
297
+ "summary": "Create an item using form-data",
298
+ "requestBody": {
299
+ "required": true,
300
+ "content": {
301
+ "multipart/form-data": {
302
+ "schema": {
303
+ "$ref": "#/components/schemas/FormDataItem"
304
+ }
305
+ }
306
+ }
307
+ },
308
+ "responses": {
309
+ "201": {
310
+ "description": "Item created with file upload"
311
+ }
312
+ }
313
+ }
314
+ },
315
+ "/items-header": {
316
+ "get": {
317
+ "tags": ["ItemsHeader"],
318
+ "summary": "Get an item with custom headers",
319
+ "parameters": [
320
+ {
321
+ "name": "id",
322
+ "in": "query",
323
+ "required": true,
324
+ "description": "ID of the item to retrieve",
325
+ "schema": {
326
+ "type": "string",
327
+ "example": "123450-getItemByIdWithHeader"
328
+ }
329
+ },
330
+ { "$ref": "#/components/parameters/clientIdHeader" },
331
+ { "$ref": "#/components/parameters/requestIdHeader" }
332
+ ],
333
+ "responses": {
334
+ "200": {
335
+ "description": "Item retrieved successfully"
336
+ }
337
+ },
338
+ "requestBody": {
339
+ "required": false
340
+ }
341
+ }
342
+ },
343
+ "/users/{userId}/posts/{postId}": {
344
+ "get": {
345
+ "operationId": "getUserPost",
346
+ "parameters": [
347
+ { "$ref": "#/components/parameters/userId" },
348
+ { "$ref": "#/components/parameters/postId" }
349
+ ],
350
+ "responses": {
351
+ "200": {
352
+ "description": "Success",
353
+ "content": {
354
+ "application/json": {
355
+ "schema": {
356
+ "$ref": "#/components/schemas/UserPost"
357
+ }
358
+ }
359
+ }
360
+ }
361
+ }
362
+ }
363
+ },
364
+ "/items-new-mandatory-headers": {
365
+ "get": {
366
+ "tags": ["ItemsHeader"],
367
+ "summary": "Retrieve a new item with mandatory headers",
368
+ "parameters": [
369
+ {
370
+ "name": "itemId",
371
+ "in": "query",
372
+ "required": true,
373
+ "description": "ID of the item to retrieve",
374
+ "schema": {
375
+ "type": "string",
376
+ "example": "67890"
377
+ }
378
+ },
379
+ { "$ref": "#/components/parameters/authTokenHeader" },
380
+ { "$ref": "#/components/parameters/correlationIdHeader" },
381
+ { "$ref": "#/components/parameters/optionalHeader" }
382
+ ],
383
+ "responses": {
384
+ "200": {
385
+ "description": "New item retrieved successfully"
386
+ },
387
+ "400": {
388
+ "description": "Invalid request"
389
+ }
390
+ }
391
+ }
392
+ }
393
+ }
394
+ }