@grafana/openapi-to-k6 0.1.2 → 0.2.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/.github/workflows/publish.yaml +2 -0
- package/.github/workflows/tests.yaml +14 -6
- package/README.md +49 -14
- package/dist/cli.js +18 -2
- package/dist/constants.js +8 -2
- package/dist/{generator.js → generator/index.js} +7 -13
- package/dist/{k6SdkClient.js → generator/k6Client.js} +148 -161
- package/examples/basic_schema/single/k6-script.sample.ts +11 -0
- package/examples/basic_schema/single/simpleAPI.ts +87 -0
- package/examples/basic_schema/split/k6-script.sample.ts +11 -0
- package/examples/basic_schema/split/simpleAPI.schemas.ts +9 -0
- package/examples/basic_schema/split/simpleAPI.ts +85 -0
- package/examples/basic_schema/tags/default.ts +85 -0
- package/examples/basic_schema/tags/k6-script.sample.ts +11 -0
- package/examples/basic_schema/tags/simpleAPI.schemas.ts +9 -0
- package/examples/form_data_schema/single/formDataAPI.ts +115 -0
- package/examples/form_data_schema/{k6-script.sample.ts → single/k6-script.sample.ts} +2 -2
- package/examples/form_data_schema/split/formDataAPI.schemas.ts +24 -0
- package/examples/form_data_schema/split/formDataAPI.ts +98 -0
- package/examples/form_data_schema/split/k6-script.sample.ts +11 -0
- package/examples/form_data_schema/tags/default.ts +98 -0
- package/examples/form_data_schema/tags/formDataAPI.schemas.ts +24 -0
- package/examples/form_data_schema/tags/k6-script.sample.ts +11 -0
- package/examples/form_url_encoded_data_schema/single/formURLEncodedAPI.ts +112 -0
- package/examples/form_url_encoded_data_schema/{k6-script.sample.ts → single/k6-script.sample.ts} +2 -2
- package/examples/form_url_encoded_data_schema/split/formURLEncodedAPI.schemas.ts +24 -0
- package/examples/form_url_encoded_data_schema/split/formURLEncodedAPI.ts +98 -0
- package/examples/form_url_encoded_data_schema/split/k6-script.sample.ts +11 -0
- package/examples/form_url_encoded_data_schema/tags/default.ts +98 -0
- package/examples/form_url_encoded_data_schema/tags/formURLEncodedAPI.schemas.ts +24 -0
- package/examples/form_url_encoded_data_schema/tags/k6-script.sample.ts +11 -0
- package/examples/form_url_encoded_data_with_query_params_schema/single/formURLEncodedAPIWithQueryParameters.ts +128 -0
- package/examples/form_url_encoded_data_with_query_params_schema/{k6-script.sample.ts → single/k6-script.sample.ts} +2 -2
- package/examples/form_url_encoded_data_with_query_params_schema/split/formURLEncodedAPIWithQueryParameters.schemas.ts +35 -0
- package/examples/form_url_encoded_data_with_query_params_schema/split/formURLEncodedAPIWithQueryParameters.ts +104 -0
- package/examples/form_url_encoded_data_with_query_params_schema/split/k6-script.sample.ts +14 -0
- package/examples/form_url_encoded_data_with_query_params_schema/tags/default.ts +104 -0
- package/examples/form_url_encoded_data_with_query_params_schema/tags/formURLEncodedAPIWithQueryParameters.schemas.ts +35 -0
- package/examples/form_url_encoded_data_with_query_params_schema/tags/k6-script.sample.ts +14 -0
- package/examples/get_request_with_path_parameters_schema/{k6-script.sample.ts → single/k6-script.sample.ts} +2 -2
- package/examples/get_request_with_path_parameters_schema/single/simpleAPI.ts +94 -0
- package/examples/get_request_with_path_parameters_schema/split/k6-script.sample.ts +11 -0
- package/examples/get_request_with_path_parameters_schema/split/simpleAPI.schemas.ts +12 -0
- package/examples/get_request_with_path_parameters_schema/split/simpleAPI.ts +90 -0
- package/examples/get_request_with_path_parameters_schema/tags/default.ts +90 -0
- package/examples/get_request_with_path_parameters_schema/tags/k6-script.sample.ts +11 -0
- package/examples/get_request_with_path_parameters_schema/tags/simpleAPI.schemas.ts +12 -0
- package/examples/headers_schema/single/headerDemoAPI.ts +202 -0
- package/examples/headers_schema/{k6-script.sample.ts → single/k6-script.sample.ts} +2 -2
- package/examples/headers_schema/split/headerDemoAPI.schemas.ts +32 -0
- package/examples/headers_schema/split/headerDemoAPI.ts +184 -0
- package/examples/headers_schema/split/k6-script.sample.ts +25 -0
- package/examples/headers_schema/tags/default.ts +182 -0
- package/examples/headers_schema/tags/headerDemoAPI.schemas.ts +32 -0
- package/examples/headers_schema/tags/k6-script.sample.ts +25 -0
- package/examples/no_title_schema/{k6-script.sample.ts → single/k6-script.sample.ts} +2 -2
- package/examples/no_title_schema/single/k6Client.ts +87 -0
- package/examples/{basic_schema → no_title_schema/split}/k6-script.sample.ts +2 -2
- package/examples/no_title_schema/split/k6Client.schemas.ts +9 -0
- package/examples/no_title_schema/split/k6Client.ts +85 -0
- package/examples/no_title_schema/tags/default.ts +85 -0
- package/examples/no_title_schema/tags/k6-script.sample.ts +11 -0
- package/examples/no_title_schema/tags/k6Client.schemas.ts +9 -0
- package/examples/post_request_with_query_params/single/exampleAPI.ts +126 -0
- package/examples/post_request_with_query_params/{k6-script.sample.ts → single/k6-script.sample.ts} +2 -2
- package/examples/post_request_with_query_params/split/exampleAPI.schemas.ts +33 -0
- package/examples/post_request_with_query_params/split/exampleAPI.ts +105 -0
- package/examples/post_request_with_query_params/split/k6-script.sample.ts +14 -0
- package/examples/post_request_with_query_params/tags/default.ts +105 -0
- package/examples/post_request_with_query_params/tags/exampleAPI.schemas.ts +33 -0
- package/examples/post_request_with_query_params/tags/k6-script.sample.ts +14 -0
- package/examples/query_params_schema/single/exampleAPI.ts +120 -0
- package/examples/query_params_schema/{k6-script.sample.ts → single/k6-script.sample.ts} +2 -2
- package/examples/query_params_schema/split/exampleAPI.schemas.ts +37 -0
- package/examples/query_params_schema/split/exampleAPI.ts +94 -0
- package/examples/query_params_schema/split/k6-script.sample.ts +11 -0
- package/examples/query_params_schema/tags/default.ts +94 -0
- package/examples/query_params_schema/tags/exampleAPI.schemas.ts +37 -0
- package/examples/query_params_schema/tags/k6-script.sample.ts +11 -0
- package/examples/simple_post_request_schema/{exampleAPI.ts → single/exampleAPI.ts} +49 -49
- package/examples/simple_post_request_schema/{k6-script.sample.ts → single/k6-script.sample.ts} +2 -2
- package/examples/simple_post_request_schema/split/exampleAPI.schemas.ts +47 -0
- package/examples/simple_post_request_schema/split/exampleAPI.ts +99 -0
- package/examples/simple_post_request_schema/split/k6-script.sample.ts +13 -0
- package/examples/simple_post_request_schema/tags/default.ts +99 -0
- package/examples/simple_post_request_schema/tags/exampleAPI.schemas.ts +47 -0
- package/examples/simple_post_request_schema/tags/k6-script.sample.ts +13 -0
- package/images/openapi-to-k6.png +0 -0
- package/package.json +2 -2
- package/src/cli.ts +28 -2
- package/src/constants.ts +7 -1
- package/src/{generator.ts → generator/index.ts} +8 -21
- package/src/{k6SdkClient.ts → generator/k6Client.ts} +174 -222
- package/src/type.d.ts +3 -4
- package/tests/e2e/schema.json +8 -0
- package/tests/e2e/{k6Script.ts → single/k6Script.ts} +7 -2
- package/tests/e2e/split/k6Script.ts +82 -0
- package/tests/e2e/tags/k6Script.ts +106 -0
- package/tests/functional-tests/fixtures/schemas/basic_schema.json +1 -4
- package/tests/functional-tests/fixtures/schemas/form_data_schema.json +4 -4
- package/tests/functional-tests/fixtures/schemas/form_url_encoded_data_schema.json +3 -3
- package/tests/functional-tests/fixtures/schemas/form_url_encoded_data_with_query_params_schema.json +2 -2
- package/tests/functional-tests/fixtures/schemas/get_request_with_path_parameters_schema.json +2 -2
- package/tests/functional-tests/fixtures/schemas/headers_schema.json +7 -8
- package/tests/functional-tests/fixtures/schemas/no_title_schema.json +2 -5
- package/tests/functional-tests/fixtures/schemas/post_request_with_query_params.json +3 -4
- package/tests/functional-tests/fixtures/schemas/query_params_schema.json +3 -3
- package/tests/functional-tests/fixtures/schemas/simple_post_request_schema.json +3 -5
- package/tests/functional-tests/generator.test.ts +46 -5
- package/examples/basic_schema/simpleAPI.ts +0 -87
- package/examples/form_data_schema/formDataAPI.ts +0 -115
- package/examples/form_url_encoded_data_schema/formURLEncodedAPI.ts +0 -114
- package/examples/form_url_encoded_data_with_query_params_schema/formURLEncodedAPIWithQueryParameters.ts +0 -130
- package/examples/get_request_with_path_parameters_schema/simpleAPI.ts +0 -94
- package/examples/headers_schema/headerDemoAPI.ts +0 -196
- package/examples/no_title_schema/K6Client.ts +0 -86
- package/examples/post_request_with_query_params/exampleAPI.ts +0 -124
- package/examples/query_params_schema/exampleAPI.ts +0 -118
- package/examples/update_examples.sh +0 -21
|
@@ -8,18 +8,14 @@ import {
|
|
|
8
8
|
ContextSpecs,
|
|
9
9
|
generateFormDataAndUrlEncodedFunction,
|
|
10
10
|
generateVerbImports,
|
|
11
|
-
GeneratorMutator,
|
|
12
11
|
GeneratorOptions,
|
|
13
12
|
GeneratorSchema,
|
|
14
13
|
GeneratorVerbOptions,
|
|
15
14
|
GetterBody,
|
|
16
|
-
GetterQueryParam,
|
|
17
15
|
GetterResponse,
|
|
18
|
-
ParamsSerializerOptions,
|
|
19
16
|
pascal,
|
|
20
17
|
sanitize,
|
|
21
18
|
toObjectString,
|
|
22
|
-
Verbs,
|
|
23
19
|
} from '@orval/core'
|
|
24
20
|
import Handlebars from 'handlebars'
|
|
25
21
|
import path from 'path'
|
|
@@ -27,16 +23,144 @@ import {
|
|
|
27
23
|
DEFAULT_SCHEMA_TITLE,
|
|
28
24
|
K6_SCRIPT_TEMPLATE,
|
|
29
25
|
SAMPLE_K6_SCRIPT_FILE_NAME,
|
|
30
|
-
} from '
|
|
31
|
-
import { getDirectoryForPath, getGeneratedClientPath } from '
|
|
32
|
-
import { logger } from '
|
|
33
|
-
import { AnalyticsData
|
|
26
|
+
} from '../constants'
|
|
27
|
+
import { getDirectoryForPath, getGeneratedClientPath } from '../helper'
|
|
28
|
+
import { logger } from '../logger'
|
|
29
|
+
import { AnalyticsData } from '../type'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* In case the supplied schema does not have a title set, it will set the default title to ensure
|
|
33
|
+
* proper client generation
|
|
34
|
+
*
|
|
35
|
+
* @param context - The context object containing the schema details
|
|
36
|
+
*/
|
|
37
|
+
function _setDefaultSchemaTitle(context: ContextSpecs) {
|
|
38
|
+
const schemaDetails = context.specs[context.specKey]
|
|
39
|
+
if (schemaDetails && !schemaDetails.info.title) {
|
|
40
|
+
schemaDetails.info.title = DEFAULT_SCHEMA_TITLE
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function _generateResponseTypeDefinition(response: GetterResponse): string {
|
|
45
|
+
let responseDataType = ''
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
response.definition.success &&
|
|
49
|
+
!['any', 'unknown'].includes(response.definition.success)
|
|
50
|
+
) {
|
|
51
|
+
responseDataType += response.definition.success
|
|
52
|
+
} else {
|
|
53
|
+
responseDataType += 'ResponseBody'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return `{
|
|
57
|
+
response: Response
|
|
58
|
+
data: ${responseDataType}
|
|
59
|
+
}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function _getRequestParametersMergerFunctionImplementation() {
|
|
63
|
+
return `/**
|
|
64
|
+
* Merges the provided request parameters with default parameters for the client.
|
|
65
|
+
*
|
|
66
|
+
* @param {Params} requestParameters - The parameters provided specifically for the request
|
|
67
|
+
* @param {Params} commonRequestParameters - Common parameters for all requests
|
|
68
|
+
* @returns {Params} - The merged parameters
|
|
69
|
+
*/
|
|
70
|
+
private _mergeRequestParameters (requestParameters?: Params, commonRequestParameters?: Params): Params {
|
|
71
|
+
return {
|
|
72
|
+
...commonRequestParameters, // Default to common parameters
|
|
73
|
+
...requestParameters, // Override with request-specific parameters
|
|
74
|
+
headers: {
|
|
75
|
+
...commonRequestParameters?.headers || {}, // Ensure headers are defined
|
|
76
|
+
...requestParameters?.headers || {},
|
|
77
|
+
},
|
|
78
|
+
cookies: {
|
|
79
|
+
...commonRequestParameters?.cookies || {}, // Ensure cookies are defined
|
|
80
|
+
...requestParameters?.cookies || {},
|
|
81
|
+
},
|
|
82
|
+
tags: {
|
|
83
|
+
...commonRequestParameters?.tags || {}, // Ensure tags are defined
|
|
84
|
+
...requestParameters?.tags || {},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
};`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const _getRequestParamsValue = ({
|
|
91
|
+
response,
|
|
92
|
+
queryParams,
|
|
93
|
+
headers,
|
|
94
|
+
body,
|
|
95
|
+
}: {
|
|
96
|
+
response: GetterResponse
|
|
97
|
+
body: GetterBody
|
|
98
|
+
queryParams?: GeneratorSchema
|
|
99
|
+
headers?: GeneratorSchema
|
|
100
|
+
}) => {
|
|
101
|
+
if (!queryParams && !headers && !response.isBlob && !body.contentType) {
|
|
102
|
+
// No parameters to merge, return the request parameters directly
|
|
103
|
+
return 'mergedRequestParameters'
|
|
104
|
+
}
|
|
34
105
|
|
|
35
|
-
|
|
36
|
-
// and the return type definition
|
|
37
|
-
const returnTypesToWrite: Map<string, string> = new Map()
|
|
106
|
+
let value = '\n ...mergedRequestParameters,'
|
|
38
107
|
|
|
39
|
-
|
|
108
|
+
if (response.isBlob) {
|
|
109
|
+
value += `\n responseType: 'binary',`
|
|
110
|
+
}
|
|
111
|
+
// Expand the headers
|
|
112
|
+
if (body.contentType || headers) {
|
|
113
|
+
let headersValue = `\n headers: {`
|
|
114
|
+
if (body.contentType) {
|
|
115
|
+
if (body.formData) {
|
|
116
|
+
headersValue += `\n'Content-Type': '${body.contentType}; boundary=' + formData.boundary,`
|
|
117
|
+
} else {
|
|
118
|
+
headersValue += `\n'Content-Type': '${body.contentType}',`
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (headers) {
|
|
123
|
+
headersValue += `\n// In the schema, headers can be of any type like number but k6 accepts only strings as headers, hence converting all headers to string`
|
|
124
|
+
headersValue += `\n...Object.fromEntries(Object.entries(headers || {}).map(([key, value]) => [key, String(value)])),`
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
headersValue += `\n...mergedRequestParameters?.headers},`
|
|
128
|
+
value += headersValue
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return `{${value}}`
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const _getK6RequestOptions = (verbOptions: GeneratorVerbOptions) => {
|
|
135
|
+
const { body, headers, queryParams, response, verb } = verbOptions
|
|
136
|
+
let fetchBodyOption = 'undefined'
|
|
137
|
+
|
|
138
|
+
if (body.formData) {
|
|
139
|
+
// Use the FormData.body() method to get the body of the request
|
|
140
|
+
fetchBodyOption = 'formData.body()'
|
|
141
|
+
} else if (body.formUrlEncoded || body.implementation) {
|
|
142
|
+
fetchBodyOption = `JSON.stringify(${body.implementation})`
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Generate the params input for the call
|
|
146
|
+
|
|
147
|
+
const requestParametersValue = _getRequestParamsValue({
|
|
148
|
+
response,
|
|
149
|
+
body,
|
|
150
|
+
headers: headers?.schema,
|
|
151
|
+
queryParams: queryParams?.schema,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// Sample output
|
|
155
|
+
// 'GET', 'http://test.com/route', <body>, <options>
|
|
156
|
+
|
|
157
|
+
return `"${verb.toUpperCase()}",
|
|
158
|
+
url.toString(),
|
|
159
|
+
${fetchBodyOption},
|
|
160
|
+
${requestParametersValue}`
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const getK6Dependencies: ClientDependenciesBuilder = () => [
|
|
40
164
|
{
|
|
41
165
|
exports: [
|
|
42
166
|
{
|
|
@@ -81,59 +205,21 @@ export const getK6Dependencies: ClientDependenciesBuilder = () => [
|
|
|
81
205
|
},
|
|
82
206
|
]
|
|
83
207
|
|
|
84
|
-
function getSchemaTitleFromContext(context: ContextSpecs) {
|
|
85
|
-
const specData = Object.values(context.specs)
|
|
86
|
-
|
|
87
|
-
let schemaTitle
|
|
88
|
-
|
|
89
|
-
if (specData[0]) {
|
|
90
|
-
schemaTitle = specData[0].info.title
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
schemaTitle ??= DEFAULT_SCHEMA_TITLE
|
|
94
|
-
|
|
95
|
-
return schemaTitle
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function _generateResponseTypeName(operationName: string): string {
|
|
99
|
-
return `${pascal(operationName)}Response`
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function _generateResponseTypeDefinition(
|
|
103
|
-
operationName: string,
|
|
104
|
-
response: GetterResponse
|
|
105
|
-
): string {
|
|
106
|
-
const typeName = _generateResponseTypeName(operationName)
|
|
107
|
-
let responseDataType = ''
|
|
108
|
-
|
|
109
|
-
if (response.definition.success) {
|
|
110
|
-
responseDataType += response.definition.success + ' | '
|
|
111
|
-
}
|
|
112
|
-
responseDataType += 'ResponseBody'
|
|
113
|
-
|
|
114
|
-
return `export type ${typeName} = {
|
|
115
|
-
response: Response
|
|
116
|
-
data: ${responseDataType}
|
|
117
|
-
};`
|
|
118
|
-
}
|
|
119
|
-
|
|
120
208
|
const generateK6Implementation = (
|
|
121
|
-
|
|
122
|
-
|
|
209
|
+
verbOptions: GeneratorVerbOptions,
|
|
210
|
+
{ route }: GeneratorOptions,
|
|
211
|
+
analyticsData?: AnalyticsData
|
|
212
|
+
) => {
|
|
213
|
+
const {
|
|
123
214
|
queryParams,
|
|
124
215
|
operationName,
|
|
125
216
|
response,
|
|
126
217
|
body,
|
|
127
218
|
props,
|
|
128
219
|
verb,
|
|
129
|
-
override,
|
|
130
220
|
formData,
|
|
131
221
|
formUrlEncoded,
|
|
132
|
-
|
|
133
|
-
}: GeneratorVerbOptions,
|
|
134
|
-
{ route }: GeneratorOptions,
|
|
135
|
-
analyticsData?: AnalyticsData
|
|
136
|
-
) => {
|
|
222
|
+
} = verbOptions
|
|
137
223
|
if (analyticsData) {
|
|
138
224
|
analyticsData.generatedRequestsCount[verb] += 1
|
|
139
225
|
}
|
|
@@ -146,40 +232,24 @@ const generateK6Implementation = (
|
|
|
146
232
|
isFormUrlEncoded: false,
|
|
147
233
|
})
|
|
148
234
|
|
|
149
|
-
|
|
150
|
-
returnTypesToWrite.set(
|
|
151
|
-
operationName,
|
|
152
|
-
_generateResponseTypeDefinition(operationName, response)
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
let url = `cleanBaseUrl + \`${route}\``
|
|
235
|
+
let url = `this.cleanBaseUrl + \`${route}\``
|
|
156
236
|
|
|
157
237
|
if (queryParams) {
|
|
158
238
|
url += '+`?${new URLSearchParams(params).toString()}`'
|
|
159
239
|
}
|
|
160
240
|
const urlGeneration = `const url = new URL(${url});`
|
|
161
241
|
|
|
162
|
-
const options =
|
|
163
|
-
route,
|
|
164
|
-
body,
|
|
165
|
-
headers,
|
|
166
|
-
queryParams,
|
|
167
|
-
response,
|
|
168
|
-
verb,
|
|
169
|
-
requestOptions: override?.requestOptions,
|
|
170
|
-
paramsSerializer,
|
|
171
|
-
paramsSerializerOptions: override?.paramsSerializerOptions,
|
|
172
|
-
})
|
|
242
|
+
const options = _getK6RequestOptions(verbOptions)
|
|
173
243
|
|
|
174
|
-
return
|
|
244
|
+
return `${operationName}(\n ${toObjectString(props, 'implementation')} requestParameters?: Params): ${_generateResponseTypeDefinition(response)} {\n${bodyForm}
|
|
175
245
|
${urlGeneration}
|
|
176
|
-
const mergedRequestParameters = _mergeRequestParameters(requestParameters || {},
|
|
246
|
+
const mergedRequestParameters = this._mergeRequestParameters(requestParameters || {}, this.commonRequestParameters);
|
|
177
247
|
const response = http.request(${options});
|
|
178
248
|
let data;
|
|
179
249
|
|
|
180
250
|
try {
|
|
181
251
|
data = response.json();
|
|
182
|
-
} catch
|
|
252
|
+
} catch {
|
|
183
253
|
data = response.body;
|
|
184
254
|
}
|
|
185
255
|
return {
|
|
@@ -190,154 +260,38 @@ const generateK6Implementation = (
|
|
|
190
260
|
`
|
|
191
261
|
}
|
|
192
262
|
|
|
193
|
-
|
|
194
|
-
route: string
|
|
195
|
-
body: GetterBody
|
|
196
|
-
headers?: GetterQueryParam
|
|
197
|
-
queryParams?: GetterQueryParam
|
|
198
|
-
response: GetterResponse
|
|
199
|
-
verb: Verbs
|
|
200
|
-
requestOptions?: object | boolean
|
|
201
|
-
isVue?: boolean
|
|
202
|
-
paramsSerializer?: GeneratorMutator
|
|
203
|
-
paramsSerializerOptions?: ParamsSerializerOptions
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const getParamsInputValue = ({
|
|
207
|
-
response,
|
|
208
|
-
queryParams,
|
|
209
|
-
headers,
|
|
210
|
-
body,
|
|
211
|
-
}: {
|
|
212
|
-
response: GetterResponse
|
|
213
|
-
body: GetterBody
|
|
214
|
-
queryParams?: GeneratorSchema
|
|
215
|
-
headers?: GeneratorSchema
|
|
216
|
-
}) => {
|
|
217
|
-
if (!queryParams && !headers && !response.isBlob && !body.contentType) {
|
|
218
|
-
// No parameters to merge, return the request parameters directly
|
|
219
|
-
return 'mergedRequestParameters'
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
let value = '\n ...mergedRequestParameters,'
|
|
223
|
-
|
|
224
|
-
if (response.isBlob) {
|
|
225
|
-
value += `\n responseType: 'binary',`
|
|
226
|
-
}
|
|
227
|
-
// Expand the headers
|
|
228
|
-
if (body.contentType || headers) {
|
|
229
|
-
let headersValue = `\n headers: {`
|
|
230
|
-
if (body.contentType) {
|
|
231
|
-
if (body.formData) {
|
|
232
|
-
headersValue += `\n'Content-Type': '${body.contentType}; boundary=' + formData.boundary,`
|
|
233
|
-
} else {
|
|
234
|
-
headersValue += `\n'Content-Type': '${body.contentType}',`
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (headers) {
|
|
239
|
-
headersValue += `\n...headers,`
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
headersValue += `\n...mergedRequestParameters?.headers},`
|
|
243
|
-
value += headersValue
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
return `{${value}}`
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const getK6RequestOptions = (options: OptionsInput) => {
|
|
250
|
-
const { body, headers, queryParams, response, verb } = options
|
|
251
|
-
|
|
252
|
-
let fetchBodyOption = 'undefined'
|
|
253
|
-
|
|
254
|
-
if (body.formData) {
|
|
255
|
-
// Use the FormData.body() method to get the body of the request
|
|
256
|
-
fetchBodyOption = 'formData.body()'
|
|
257
|
-
} else if (body.formUrlEncoded || body.implementation) {
|
|
258
|
-
fetchBodyOption = `JSON.stringify(${body.implementation})`
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Generate the params input for the call
|
|
262
|
-
|
|
263
|
-
const paramsValue = getParamsInputValue({
|
|
264
|
-
response,
|
|
265
|
-
body,
|
|
266
|
-
headers: headers?.schema,
|
|
267
|
-
queryParams: queryParams?.schema,
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
// Sample output
|
|
271
|
-
// 'GET', 'http://test.com/route', <body>, <options>
|
|
272
|
-
|
|
273
|
-
return `"${verb.toUpperCase()}",
|
|
274
|
-
url.toString(),
|
|
275
|
-
${fetchBodyOption},
|
|
276
|
-
${paramsValue}`
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function _getRequestParametersMergerFunctionImplementation() {
|
|
280
|
-
return `/**
|
|
281
|
-
* Merges the provided request parameters with default parameters for the client.
|
|
282
|
-
*
|
|
283
|
-
* @param {Params} requestParameters - The parameters provided specifically for the request
|
|
284
|
-
* @param {Params} commonRequestParameters - Common parameters for all requests
|
|
285
|
-
* @returns {Params} - The merged parameters
|
|
286
|
-
*/
|
|
287
|
-
const _mergeRequestParameters = (requestParameters?: Params, commonRequestParameters?: Params): Params => {
|
|
288
|
-
return {
|
|
289
|
-
...commonRequestParameters, // Default to common parameters
|
|
290
|
-
...requestParameters, // Override with request-specific parameters
|
|
291
|
-
headers: {
|
|
292
|
-
...commonRequestParameters?.headers || {}, // Ensure headers are defined
|
|
293
|
-
...requestParameters?.headers || {},
|
|
294
|
-
},
|
|
295
|
-
cookies: {
|
|
296
|
-
...commonRequestParameters?.cookies || {}, // Ensure cookies are defined
|
|
297
|
-
...requestParameters?.cookies || {},
|
|
298
|
-
},
|
|
299
|
-
tags: {
|
|
300
|
-
...commonRequestParameters?.tags || {}, // Ensure tags are defined
|
|
301
|
-
...requestParameters?.tags || {},
|
|
302
|
-
},
|
|
303
|
-
};
|
|
304
|
-
};`
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
export const generateTitle: ClientTitleBuilder = (title) => {
|
|
263
|
+
const generateTitle: ClientTitleBuilder = (title) => {
|
|
308
264
|
const sanTitle = sanitize(title || DEFAULT_SCHEMA_TITLE)
|
|
309
|
-
return
|
|
265
|
+
return `${pascal(sanTitle)}Client`
|
|
310
266
|
}
|
|
311
267
|
|
|
312
|
-
|
|
313
|
-
const clientOptionsTypeName = `${pascal(title)}Options`
|
|
268
|
+
const generateK6Header: ClientHeaderBuilder = ({ title }) => {
|
|
314
269
|
return `
|
|
315
|
-
|
|
270
|
+
/**
|
|
271
|
+
* This is the base client to use for interacting with the API.
|
|
272
|
+
*/
|
|
273
|
+
export class ${title} {
|
|
274
|
+
private cleanBaseUrl: string;
|
|
275
|
+
private commonRequestParameters: Params;
|
|
276
|
+
|
|
277
|
+
constructor (clientOptions: {
|
|
316
278
|
baseUrl: string,
|
|
317
279
|
commonRequestParameters?: Params
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
*/
|
|
323
|
-
export const ${title} = (clientOptions: ${clientOptionsTypeName}) => {\n
|
|
324
|
-
const cleanBaseUrl = clientOptions.baseUrl.replace(/\\/+$/, '');\n`
|
|
280
|
+
}) {
|
|
281
|
+
this.cleanBaseUrl = clientOptions.baseUrl.replace(/\\/+$/, '');\n
|
|
282
|
+
}\n
|
|
283
|
+
`
|
|
325
284
|
}
|
|
326
285
|
|
|
327
|
-
|
|
328
|
-
|
|
286
|
+
const generateFooter: ClientFooterBuilder = () => {
|
|
287
|
+
// Add function definition for merging request parameters
|
|
288
|
+
const footer = `
|
|
329
289
|
|
|
330
|
-
|
|
290
|
+
${_getRequestParametersMergerFunctionImplementation()}
|
|
331
291
|
|
|
332
|
-
|
|
333
|
-
if (returnTypesToWrite.has(operationName)) {
|
|
334
|
-
footer += returnTypesToWrite.get(operationName) + '\n'
|
|
335
|
-
}
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
// Add function definition for merging request parameters
|
|
339
|
-
footer += `\n\n${_getRequestParametersMergerFunctionImplementation()}\n`
|
|
292
|
+
}
|
|
340
293
|
|
|
294
|
+
`
|
|
341
295
|
return footer
|
|
342
296
|
}
|
|
343
297
|
|
|
@@ -346,7 +300,8 @@ const k6ScriptBuilder: ClientExtraFilesBuilder = async (
|
|
|
346
300
|
output,
|
|
347
301
|
context
|
|
348
302
|
) => {
|
|
349
|
-
const schemaTitle =
|
|
303
|
+
const schemaTitle =
|
|
304
|
+
context.specs[context.specKey]?.info.title || DEFAULT_SCHEMA_TITLE
|
|
350
305
|
const {
|
|
351
306
|
path: pathOfGeneratedClient,
|
|
352
307
|
filename,
|
|
@@ -400,21 +355,19 @@ const k6ScriptBuilder: ClientExtraFilesBuilder = async (
|
|
|
400
355
|
]
|
|
401
356
|
}
|
|
402
357
|
|
|
403
|
-
function getK6Client(
|
|
404
|
-
schemaDetails: SchemaDetails,
|
|
405
|
-
analyticsData?: AnalyticsData
|
|
406
|
-
) {
|
|
358
|
+
function getK6Client(analyticsData?: AnalyticsData) {
|
|
407
359
|
return function (
|
|
408
360
|
verbOptions: GeneratorVerbOptions,
|
|
409
361
|
options: GeneratorOptions
|
|
410
362
|
) {
|
|
363
|
+
_setDefaultSchemaTitle(options.context)
|
|
364
|
+
|
|
411
365
|
const imports = generateVerbImports(verbOptions)
|
|
412
366
|
const implementation = generateK6Implementation(
|
|
413
367
|
verbOptions,
|
|
414
368
|
options,
|
|
415
369
|
analyticsData
|
|
416
370
|
)
|
|
417
|
-
schemaDetails.title = getSchemaTitleFromContext(options.context)
|
|
418
371
|
const specData = Object.values(options.context.specs)
|
|
419
372
|
if (specData[0]) {
|
|
420
373
|
if (analyticsData) {
|
|
@@ -427,12 +380,11 @@ function getK6Client(
|
|
|
427
380
|
}
|
|
428
381
|
|
|
429
382
|
export function getK6ClientBuilder(
|
|
430
|
-
schemaDetails: SchemaDetails,
|
|
431
383
|
shouldGenerateSampleK6Script?: boolean,
|
|
432
384
|
analyticsData?: AnalyticsData
|
|
433
385
|
): ClientGeneratorsBuilder {
|
|
434
386
|
return {
|
|
435
|
-
client: getK6Client(
|
|
387
|
+
client: getK6Client(analyticsData),
|
|
436
388
|
header: generateK6Header,
|
|
437
389
|
dependencies: getK6Dependencies,
|
|
438
390
|
footer: generateFooter,
|
package/src/type.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Mode } from './constants'
|
|
2
|
+
|
|
1
3
|
export interface PackageDetails {
|
|
2
4
|
name: string
|
|
3
5
|
commandName: string
|
|
@@ -5,10 +7,6 @@ export interface PackageDetails {
|
|
|
5
7
|
version: string
|
|
6
8
|
}
|
|
7
9
|
|
|
8
|
-
export interface SchemaDetails {
|
|
9
|
-
title: string
|
|
10
|
-
}
|
|
11
|
-
|
|
12
10
|
export interface AnalyticsData {
|
|
13
11
|
generatedRequestsCount: {
|
|
14
12
|
post: number
|
|
@@ -31,4 +29,5 @@ export interface GenerateK6SDKOptions {
|
|
|
31
29
|
outputDir: string
|
|
32
30
|
shouldGenerateSampleK6Script?: boolean
|
|
33
31
|
analyticsData?: AnalyticsData
|
|
32
|
+
mode: Mode
|
|
34
33
|
}
|
package/tests/e2e/schema.json
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"paths": {
|
|
9
9
|
"/items/{id}": {
|
|
10
10
|
"get": {
|
|
11
|
+
"tags": ["Items"],
|
|
11
12
|
"summary": "Get an item by ID",
|
|
12
13
|
"parameters": [
|
|
13
14
|
{
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
}
|
|
38
39
|
},
|
|
39
40
|
"post": {
|
|
41
|
+
"tags": ["Items"],
|
|
40
42
|
"summary": "Create an item by ID",
|
|
41
43
|
"parameters": [
|
|
42
44
|
{
|
|
@@ -74,6 +76,7 @@
|
|
|
74
76
|
}
|
|
75
77
|
},
|
|
76
78
|
"put": {
|
|
79
|
+
"tags": ["Items"],
|
|
77
80
|
"summary": "Update an item by ID",
|
|
78
81
|
"parameters": [
|
|
79
82
|
{
|
|
@@ -111,6 +114,7 @@
|
|
|
111
114
|
}
|
|
112
115
|
},
|
|
113
116
|
"patch": {
|
|
117
|
+
"tags": ["Items"],
|
|
114
118
|
"summary": "Partially update an item by ID",
|
|
115
119
|
"parameters": [
|
|
116
120
|
{
|
|
@@ -145,6 +149,7 @@
|
|
|
145
149
|
}
|
|
146
150
|
},
|
|
147
151
|
"delete": {
|
|
152
|
+
"tags": ["Items"],
|
|
148
153
|
"summary": "Delete an item by ID",
|
|
149
154
|
"parameters": [
|
|
150
155
|
{
|
|
@@ -164,6 +169,7 @@
|
|
|
164
169
|
}
|
|
165
170
|
},
|
|
166
171
|
"head": {
|
|
172
|
+
"tags": ["Items"],
|
|
167
173
|
"summary": "Check if item exists",
|
|
168
174
|
"parameters": [
|
|
169
175
|
{
|
|
@@ -188,6 +194,7 @@
|
|
|
188
194
|
},
|
|
189
195
|
"/items-form-url-encoded": {
|
|
190
196
|
"post": {
|
|
197
|
+
"tags": ["ItemsForm", "Items"],
|
|
191
198
|
"summary": "Create an item using form-urlencoded data",
|
|
192
199
|
"requestBody": {
|
|
193
200
|
"required": true,
|
|
@@ -217,6 +224,7 @@
|
|
|
217
224
|
},
|
|
218
225
|
"/items-form-data": {
|
|
219
226
|
"post": {
|
|
227
|
+
"tags": ["ItemsForm", "Items"],
|
|
220
228
|
"summary": "Create an item using form-data",
|
|
221
229
|
"requestBody": {
|
|
222
230
|
"required": true,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable import/no-unresolved */
|
|
2
2
|
import { check } from 'k6'
|
|
3
|
-
import {
|
|
3
|
+
import { ComprehensiveAPIClient } from './sdk.ts'
|
|
4
4
|
/* eslint-enable import/no-unresolved */
|
|
5
5
|
|
|
6
6
|
const baseUrl = 'http://localhost:3000'
|
|
7
|
-
const client =
|
|
7
|
+
const client = new ComprehensiveAPIClient({ baseUrl })
|
|
8
8
|
|
|
9
9
|
export const options = {
|
|
10
10
|
thresholds: {
|
|
@@ -74,4 +74,9 @@ export default function () {
|
|
|
74
74
|
'multipart/form-data; boundary=---'
|
|
75
75
|
),
|
|
76
76
|
})
|
|
77
|
+
|
|
78
|
+
const getItemsHeaderResponseData = client.getItemsHeader({
|
|
79
|
+
id: 'test',
|
|
80
|
+
})
|
|
81
|
+
checkResponseStatus(getItemsHeaderResponseData.response, 200)
|
|
77
82
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/* eslint-disable import/no-unresolved */
|
|
2
|
+
import { check } from 'k6'
|
|
3
|
+
import { ComprehensiveAPIClient } from './sdk.ts'
|
|
4
|
+
/* eslint-enable import/no-unresolved */
|
|
5
|
+
|
|
6
|
+
const baseUrl = 'http://localhost:3000'
|
|
7
|
+
const client = new ComprehensiveAPIClient({ baseUrl })
|
|
8
|
+
|
|
9
|
+
export const options = {
|
|
10
|
+
thresholds: {
|
|
11
|
+
// the rate of successful checks should be higher than 90%
|
|
12
|
+
checks: ['rate>=1'],
|
|
13
|
+
},
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function checkResponseStatus(response, expectedStatus) {
|
|
17
|
+
const result = check(response, {
|
|
18
|
+
[`status is ${expectedStatus}`]: (r) => r.status === expectedStatus,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
if (!result) {
|
|
22
|
+
console.error(
|
|
23
|
+
`Check failed! Expected status ${expectedStatus} but got ${response.status}. Following is the response:`
|
|
24
|
+
)
|
|
25
|
+
console.log(JSON.stringify(response, null, 2))
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default function () {
|
|
30
|
+
const getResponseData = client.getItemsId('1')
|
|
31
|
+
checkResponseStatus(getResponseData.response, 200)
|
|
32
|
+
|
|
33
|
+
const postResponseData = client.postItemsId('1', {
|
|
34
|
+
name: 'string',
|
|
35
|
+
})
|
|
36
|
+
checkResponseStatus(postResponseData.response, 201)
|
|
37
|
+
|
|
38
|
+
const putResponseData = client.putItemsId('1', {
|
|
39
|
+
description: 'string',
|
|
40
|
+
})
|
|
41
|
+
checkResponseStatus(putResponseData.response, 200)
|
|
42
|
+
|
|
43
|
+
const deleteResponseData = client.deleteItemsId('1')
|
|
44
|
+
checkResponseStatus(deleteResponseData.response, 204)
|
|
45
|
+
|
|
46
|
+
const patchResponseData = client.patchItemsId('1', {
|
|
47
|
+
name: 'string',
|
|
48
|
+
})
|
|
49
|
+
checkResponseStatus(patchResponseData.response, 200)
|
|
50
|
+
|
|
51
|
+
const headResponseData = client.headItemsId('1')
|
|
52
|
+
checkResponseStatus(headResponseData.response, 200)
|
|
53
|
+
|
|
54
|
+
const postFormUrlEncodedResponseData = client.postItemsFormUrlEncoded({
|
|
55
|
+
name: 'string',
|
|
56
|
+
})
|
|
57
|
+
checkResponseStatus(postFormUrlEncodedResponseData.response, 201)
|
|
58
|
+
// Should add x-www-form-urlencoded content type header in request
|
|
59
|
+
check(postFormUrlEncodedResponseData.response, {
|
|
60
|
+
'has x-www-form-urlencoded content type header': (r) =>
|
|
61
|
+
r.request.headers['Content-Type'][0] ===
|
|
62
|
+
'application/x-www-form-urlencoded',
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const postFormDataResponseData = client.postItemsFormData({
|
|
66
|
+
name: 'string',
|
|
67
|
+
})
|
|
68
|
+
checkResponseStatus(postFormDataResponseData.response, 201)
|
|
69
|
+
|
|
70
|
+
// Should add multipart/form-data content type header in request with boundary
|
|
71
|
+
check(postFormDataResponseData.response, {
|
|
72
|
+
'has multipart/form-data content type header with bounday': (r) =>
|
|
73
|
+
r.request.headers['Content-Type'][0].includes(
|
|
74
|
+
'multipart/form-data; boundary=---'
|
|
75
|
+
),
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const getItemsHeaderResponseData = client.getItemsHeader({
|
|
79
|
+
id: 'test',
|
|
80
|
+
})
|
|
81
|
+
checkResponseStatus(getItemsHeaderResponseData.response, 200)
|
|
82
|
+
}
|