Skip to content

Commit 0735716

Browse files
authored
Merge pull request #110 from FlowTestAI/advanced-openapi-spec-parser
feat: Advanced openapi spec parser
2 parents 3a14771 + 780f3c2 commit 0735716

10 files changed

Lines changed: 601 additions & 65 deletions

File tree

.changeset/friendly-feet-sell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"flowtestai": minor
3+
---
4+
5+
generate sample request body and parameter values

packages/flowtest-electron/src/ipc/collection.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const createDirectory = require('../utils/filemanager/createdirectory');
88
const deleteDirectory = require('../utils/filemanager/deletedirectory');
99
const uuidv4 = require('uuid').v4;
1010
const Collections = require('../store/collection');
11-
const parseOpenAPISpec = require('../utils/collection');
11+
const { parseOpenAPISpec } = require('../utils/collection');
1212
const { isDirectory, pathExists } = require('../utils/filemanager/filesystem');
1313
const createFile = require('../utils/filemanager/createfile');
1414
const updateFile = require('../utils/filemanager/updatefile');

packages/flowtest-electron/src/utils/collection.js

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
const { generateRequestBodyExample } = require('./generate-request-body');
2+
const {
3+
generateQueryParamsExample,
4+
generateParameterExample,
5+
generatePathParamsExample,
6+
} = require('./generate-request-parameters');
7+
18
const computeUrl = (baseUrl, path) => {
29
if (baseUrl.charAt(baseUrl.length - 1) === '/' && path.charAt(0) === '/') {
310
return baseUrl + path.substring(1, path.length);
@@ -28,30 +35,68 @@ const parseOpenAPISpec = (collection) => {
2835
const tags = request['tags'];
2936
var url = replaceSingleToDoubleCurlyBraces(computeUrl(baseUrl, path));
3037
var variables = {};
38+
var requestBody = {};
39+
const pathParameters = [];
40+
const queryParameters = [];
3141

32-
// console.log(operationId)
33-
// Get is easy, others are hard
34-
if (requestType.toUpperCase() === 'GET' && request['parameters']) {
42+
if (request['parameters']) {
3543
let firstQueryParam = true;
3644
request['parameters'].map((value, _) => {
37-
// path parameters are included in url
38-
// handle multiple parameters
39-
// allow different type of variables in request node like string, int, array etc...
4045
if (value['in'] === 'query') {
4146
if (firstQueryParam) {
4247
url = url.concat(`?${value['name']}={{${value['name']}}}`);
4348
firstQueryParam = false;
4449
} else {
4550
url = url.concat(`&${value['name']}={{${value['name']}}}`);
4651
}
52+
queryParameters.push(value);
53+
}
54+
55+
if (value['in'] === 'path') {
56+
pathParameters.push(value);
4757
}
4858
});
4959
}
5060

61+
if (queryParameters.length > 0) {
62+
const res = generateQueryParamsExample(queryParameters);
63+
Array.from(res.entries()).map(([key, value], index) => {
64+
variables[key] = {
65+
type: typeof value,
66+
value,
67+
};
68+
});
69+
}
70+
71+
if (pathParameters.length > 0) {
72+
const res = generatePathParamsExample(pathParameters);
73+
Array.from(res.entries()).map(([key, value], index) => {
74+
variables[key] = {
75+
type: typeof value,
76+
value,
77+
};
78+
});
79+
}
80+
5181
if (request['requestBody']) {
52-
if (request['requestBody']['application/json']) {
53-
// console.log('requestBody: ', request["requestBody"]["content"]["schema"])
54-
// generate an example to be used in request body
82+
if (request['requestBody']['content']['application/json']) {
83+
requestBody = {
84+
type: 'raw-json',
85+
body: JSON.stringify(
86+
generateRequestBodyExample(request['requestBody']['content']['application/json']['schema']),
87+
),
88+
};
89+
}
90+
91+
if (request['requestBody']['content']['multipart/form-data']) {
92+
requestBody = {
93+
type: 'form-data',
94+
body: {
95+
key: '',
96+
value: '',
97+
name: '',
98+
},
99+
};
55100
}
56101
}
57102

@@ -61,8 +106,10 @@ const parseOpenAPISpec = (collection) => {
61106
operationId: operationId,
62107
requestType: requestType.toUpperCase(),
63108
tags: tags,
109+
requestBody,
110+
preReqVars: variables,
64111
};
65-
// console.log(finalNode);
112+
66113
parsedNodes.push(finalNode);
67114
});
68115
});
@@ -72,4 +119,6 @@ const parseOpenAPISpec = (collection) => {
72119
return parsedNodes;
73120
};
74121

75-
module.exports = parseOpenAPISpec;
122+
module.exports = {
123+
parseOpenAPISpec,
124+
};
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
const { generateRequestBodyExample } = require('./generate-request-body.js');
2+
const { generatePathParamsExample, generateQueryParamsExample } = require('./generate-request-parameters.js');
3+
4+
describe('collection parser', () => {
5+
it('should generate request body example', () => {
6+
console.log(JSON.stringify(generateRequestBodyExample(userSchema), null, 2));
7+
console.log(JSON.stringify(generateRequestBodyExample(productSchema), null, 2));
8+
console.log(JSON.stringify(generateRequestBodyExample(complexSchema), null, 2));
9+
});
10+
11+
it('should generate request parameters example', () => {
12+
console.log('Path Parameters Example:', generatePathParamsExample(pathParameters).toString());
13+
console.log('Query Parameters Example:', generateQueryParamsExample(queryParameters).toString());
14+
});
15+
});
16+
17+
const userSchema = {
18+
type: 'object',
19+
properties: {
20+
id: {
21+
type: 'integer',
22+
format: 'int64',
23+
example: 1,
24+
minimum: 1,
25+
},
26+
name: {
27+
type: 'string',
28+
example: 'John Doe',
29+
minLength: 3,
30+
maxLength: 20,
31+
},
32+
email: {
33+
type: 'string',
34+
format: 'email',
35+
example: 'john.doe@example.com',
36+
},
37+
birthdate: {
38+
type: 'string',
39+
format: 'date',
40+
example: '1990-01-01',
41+
},
42+
website: {
43+
type: 'string',
44+
format: 'uri',
45+
example: 'https://johndoe.com',
46+
},
47+
role: {
48+
type: 'string',
49+
enum: ['admin', 'user', 'guest'],
50+
example: 'user',
51+
},
52+
username: {
53+
type: 'string',
54+
pattern: '^[a-zA-Z0-9]{3,}$',
55+
example: 'user123',
56+
},
57+
interests: {
58+
type: 'array',
59+
items: {
60+
type: 'string',
61+
},
62+
example: ['coding', 'reading'],
63+
},
64+
},
65+
};
66+
67+
const productSchema = {
68+
type: 'object',
69+
properties: {
70+
id: {
71+
type: 'integer',
72+
format: 'int32',
73+
example: 101,
74+
minimum: 1,
75+
maximum: 1000,
76+
},
77+
name: {
78+
type: 'string',
79+
example: 'Sample Product',
80+
minLength: 3,
81+
},
82+
price: {
83+
type: 'number',
84+
format: 'double',
85+
example: 19.99,
86+
minimum: 0,
87+
maximum: 1000,
88+
},
89+
tags: {
90+
type: 'array',
91+
items: {
92+
type: 'string',
93+
example: 'tag1',
94+
},
95+
},
96+
status: {
97+
type: 'string',
98+
enum: ['available', 'out of stock', 'discontinued'],
99+
example: 'available',
100+
},
101+
releaseDate: {
102+
type: 'string',
103+
format: 'date-time',
104+
example: '2023-01-01T00:00:00Z',
105+
},
106+
},
107+
};
108+
109+
const complexSchema = {
110+
type: 'object',
111+
properties: {
112+
name: {
113+
type: 'string',
114+
example: 'Complex Example',
115+
},
116+
detail: {
117+
oneOf: [
118+
{ type: 'string', example: 'OneOf String' },
119+
{ type: 'integer', example: 42 },
120+
],
121+
},
122+
options: {
123+
anyOf: [
124+
{ type: 'boolean', example: true },
125+
{ type: 'string', example: 'AnyOf String' },
126+
],
127+
},
128+
allDetails: {
129+
allOf: [
130+
{
131+
type: 'object',
132+
properties: {
133+
part1: {
134+
type: 'string',
135+
example: 'Part 1',
136+
},
137+
},
138+
},
139+
{
140+
type: 'object',
141+
properties: {
142+
part2: {
143+
type: 'number',
144+
example: 123.45,
145+
},
146+
},
147+
},
148+
],
149+
},
150+
},
151+
};
152+
153+
// Example usage:
154+
155+
const pathParameters = [
156+
{
157+
name: 'userId',
158+
in: 'path',
159+
required: true,
160+
schema: {
161+
type: 'integer',
162+
format: 'int64',
163+
example: 123,
164+
minimum: 1,
165+
},
166+
},
167+
{
168+
name: 'username',
169+
in: 'path',
170+
required: true,
171+
schema: {
172+
type: 'string',
173+
minLength: 3,
174+
maxLength: 20,
175+
example: 'john_doe',
176+
},
177+
},
178+
];
179+
180+
const queryParameters = [
181+
{
182+
name: 'page',
183+
in: 'query',
184+
schema: {
185+
type: 'integer',
186+
example: 1,
187+
minimum: 1,
188+
},
189+
},
190+
{
191+
name: 'limit',
192+
in: 'query',
193+
schema: {
194+
type: 'integer',
195+
example: 10,
196+
minimum: 1,
197+
maximum: 100,
198+
},
199+
},
200+
{
201+
name: 'sort',
202+
in: 'query',
203+
schema: {
204+
type: 'string',
205+
enum: ['asc', 'desc'],
206+
example: 'asc',
207+
},
208+
},
209+
{
210+
name: 'filter',
211+
in: 'query',
212+
schema: {
213+
type: 'array',
214+
items: {
215+
type: 'string',
216+
example: 'status:active',
217+
},
218+
},
219+
},
220+
{
221+
name: 'search',
222+
in: 'query',
223+
schema: {
224+
type: 'string',
225+
example: 'example',
226+
},
227+
},
228+
];

0 commit comments

Comments
 (0)