Skip to content

Commit a9b32fa

Browse files
authored
Merge pull request #130 from FlowTestAI/form-data-fix
fix form-data request type parsing
2 parents b73d448 + 1844b9b commit a9b32fa

5 files changed

Lines changed: 107 additions & 83 deletions

File tree

packages/flowtest-cli/graph/compute/requestnode.js

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -45,78 +45,73 @@ class requestNode extends Node {
4545
const finalUrl = computeVariables(this.nodeData.url, variablesDict);
4646

4747
// step 3
48-
const options = this.formulateRequest(finalUrl, variablesDict);
48+
const rawRequest = this.formulateRequest(finalUrl, variablesDict);
4949

5050
console.log(chalk.green(` ✓ `) + chalk.dim(`type = ${this.nodeData.requestType.toUpperCase()}`));
5151
console.log(chalk.green(` ✓ `) + chalk.dim(`url = ${finalUrl}`));
5252

53-
const res = await this.runHttpRequest(options);
53+
const { request, response } = await this.runHttpRequest(rawRequest);
5454

55-
if (this.nodeData.requestBody.type === 'form-data') {
56-
// we don't want to send full file value
57-
options.data.value = '<BASE64_ENCODED_FILE_DATA>';
58-
}
59-
60-
if (res.error) {
61-
console.log(chalk.red(` ✕ `) + chalk.dim(`Request failed: ${JSON.stringify(res.error)}`));
55+
if (response.error) {
56+
console.log(chalk.red(` ✕ `) + chalk.dim(`Request failed: ${JSON.stringify(response.error)}`));
6257
this.logger.add(LogLevel.ERROR, 'HTTP request failed', {
6358
type: 'requestNode',
6459
data: {
65-
request: { type: options.method, url: options.url, data: options.data },
66-
response: res.error,
60+
request,
61+
response: response.error,
6762
preReqVars: evalVariables,
6863
},
6964
});
7065
return {
7166
status: 'Failed',
7267
};
7368
} else {
74-
console.log(chalk.green(` ✓ `) + chalk.dim(`Request successful: ${JSON.stringify(res)}`));
69+
console.log(chalk.green(` ✓ `) + chalk.dim(`Request successful: ${JSON.stringify(response)}`));
7570
if (this.nodeData.postRespVars) {
76-
const evalPostRespVars = computeNodeVariables(this.nodeData.postRespVars, res.data);
71+
const evalPostRespVars = computeNodeVariables(this.nodeData.postRespVars, response.data);
7772
this.logger.add(LogLevel.INFO, 'HTTP request success', {
7873
type: 'requestNode',
7974
data: {
80-
request: { type: options.method, url: options.url, data: options.data },
81-
response: res,
75+
request,
76+
response,
8277
preReqVars: evalVariables,
8378
postRespVars: evalPostRespVars,
8479
},
8580
});
8681
return {
8782
status: 'Success',
88-
data: res.data,
83+
data: response.data,
8984
postRespVars: evalPostRespVars,
9085
};
9186
}
9287
this.logger.add(LogLevel.INFO, 'HTTP request success', {
9388
type: 'requestNode',
9489
data: {
95-
request: { type: options.method, url: options.url, data: options.data },
96-
response: res,
90+
request,
91+
response,
9792
preReqVars: evalVariables,
9893
},
9994
});
10095
return {
10196
status: 'Success',
102-
data: res.data,
97+
data: response.data,
10398
};
10499
}
105100
}
106101

107102
formulateRequest(finalUrl, variablesDict) {
108103
let restMethod = this.nodeData.requestType.toLowerCase();
109-
let contentType = 'application/json';
104+
let headers = {};
110105
let requestData = undefined;
111106

112107
if (this.nodeData.requestBody) {
113108
if (this.nodeData.requestBody.type === 'raw-json') {
114-
contentType = 'application/json';
109+
headers['content-type'] = 'application/json';
115110
requestData = this.nodeData.requestBody.body
116111
? JSON.parse(computeVariables(this.nodeData.requestBody.body, variablesDict))
117112
: JSON.parse('{}');
118113
} else if (this.nodeData.requestBody.type === 'form-data') {
119-
contentType = 'multipart/form-data';
114+
headers['content-type'] = 'multipart/form-data';
120115
const params = cloneDeep(this.nodeData.requestBody.body);
121116
requestData = params;
122117
}
@@ -125,9 +120,7 @@ class requestNode extends Node {
125120
const options = {
126121
method: restMethod,
127122
url: finalUrl,
128-
headers: {
129-
'content-type': contentType,
130-
},
123+
headers,
131124
data: requestData,
132125
};
133126

@@ -141,6 +134,7 @@ class requestNode extends Node {
141134
}
142135

143136
async runHttpRequest(request) {
137+
let requestSent;
144138
try {
145139
if (request.headers['content-type'] === 'multipart/form-data') {
146140
const formData = new FormData();
@@ -161,34 +155,51 @@ class requestNode extends Node {
161155
extend(request.headers, formData.getHeaders());
162156
}
163157

164-
// assuming 'application/json' type
165-
const options = {
158+
requestSent = {
159+
url: request.url,
160+
method: request.method,
161+
headers: request.headers,
162+
// form data obj gets serialized here so that it can be sent over wire
163+
// otherwise ipc communication errors out
164+
data: JSON.parse(JSON.stringify(request.data)),
165+
};
166+
167+
const result = await axios({
166168
...request,
167169
signal: newAbortSignal(),
168-
};
170+
});
169171

170-
const result = await axios(options);
171172
return {
172-
status: result.status,
173-
statusText: result.statusText,
174-
data: result.data,
175-
headers: result.headers,
173+
request: requestSent,
174+
response: {
175+
status: result.status,
176+
statusText: result.statusText,
177+
data: result.data,
178+
headers: result.headers,
179+
},
176180
};
177181
} catch (error) {
178182
if (error?.response) {
179183
return {
180-
error: {
181-
status: error.response.status,
182-
statusText: error.response.statusText,
183-
data: error.response.data,
184+
request: requestSent,
185+
response: {
186+
error: {
187+
status: error.response.status,
188+
statusText: error.response.statusText,
189+
data: error.response.data,
190+
headers: error.response.headers,
191+
},
184192
},
185193
};
186194
} else {
187195
return {
188-
error: {
189-
status: '',
190-
statusText: '',
191-
data: `An error occurred while running the request : ${error?.message}`,
196+
request: requestSent,
197+
response: {
198+
error: {
199+
status: '',
200+
statusText: '',
201+
data: `An error occurred while running the request : ${error?.message}`,
202+
},
192203
},
193204
};
194205
}

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

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const { stringify, parse } = require('flatted');
1919
const { deserialize, serialize } = require('../utils/flowparser/parser');
2020
const { axiosClient } = require('./axiosClient');
2121
const FormData = require('form-data');
22-
const { extend } = require('lodash');
22+
const { extend, cloneDeep } = require('lodash');
2323

2424
const collectionStore = new Collections();
2525
const flowTestAI = new FlowtestAI();
@@ -286,6 +286,7 @@ const registerRendererEventHandlers = (mainWindow, watcher) => {
286286
});
287287

288288
ipcMain.handle('renderer:run-http-request', async (event, request, collectionPath) => {
289+
let requestSent;
289290
try {
290291
if (request.headers['content-type'] === 'multipart/form-data') {
291292
const formData = new FormData();
@@ -310,34 +311,51 @@ const registerRendererEventHandlers = (mainWindow, watcher) => {
310311
extend(request.headers, formData.getHeaders());
311312
}
312313

313-
const options = {
314-
...request,
315-
signal: newAbortSignal(),
314+
requestSent = {
315+
url: request.url,
316+
method: request.method,
317+
headers: request.headers,
318+
// form data obj gets serialized here so that it can be sent over wire
319+
// otherwise ipc communication errors out
320+
data: JSON.parse(JSON.stringify(request.data)),
316321
};
317322

318-
const result = await axios(options);
323+
const result = await axios({
324+
...request,
325+
signal: newAbortSignal(),
326+
});
319327

320328
return {
321-
status: result.status,
322-
statusText: result.statusText,
323-
data: result.data,
324-
headers: result.headers,
329+
request: requestSent,
330+
response: {
331+
status: result.status,
332+
statusText: result.statusText,
333+
data: result.data,
334+
headers: result.headers,
335+
},
325336
};
326337
} catch (error) {
327338
if (error?.response) {
328339
return {
329-
error: {
330-
status: error.response.status,
331-
statusText: error.response.statusText,
332-
data: error.response.data,
340+
request: requestSent,
341+
response: {
342+
error: {
343+
status: error.response.status,
344+
statusText: error.response.statusText,
345+
data: error.response.data,
346+
headers: error.response.headers,
347+
},
333348
},
334349
};
335350
} else {
336351
return {
337-
error: {
338-
status: '',
339-
statusText: '',
340-
data: `An error occurred while running the request : ${error?.message}`,
352+
request: requestSent,
353+
response: {
354+
error: {
355+
status: '',
356+
statusText: '',
357+
data: `An error occurred while running the request : ${error?.message}`,
358+
},
341359
},
342360
};
343361
}

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,7 @@ const parseOpenAPISpec = (collection) => {
115115
if (request['requestBody']['content']['multipart/form-data']) {
116116
requestBody = {
117117
type: 'form-data',
118-
body: {
119-
key: '',
120-
value: '',
121-
name: '',
122-
},
118+
body: [],
123119
};
124120
}
125121
}

src/components/molecules/flow/graph/compute/requestnode.js

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@ class requestNode extends Node {
3030
console.debug('Evaluated Url: ', finalUrl);
3131

3232
// step 3
33-
const options = await this.formulateRequest(finalUrl, variablesDict);
33+
const rawRequest = await this.formulateRequest(finalUrl, variablesDict);
3434

35-
const res = await this.runHttpRequest(options);
35+
const { request, response } = await this.runHttpRequest(rawRequest);
3636

37-
if (res.error) {
37+
if (response.error) {
3838
this.logger.add(LogLevel.ERROR, 'HTTP request failed', {
3939
type: 'requestNode',
4040
data: {
41-
request: { type: options.method, url: options.url, data: options.data },
42-
response: res.error,
41+
request,
42+
response: response.error,
4343
preReqVars: evalVariables,
4444
},
4545
});
@@ -48,50 +48,50 @@ class requestNode extends Node {
4848
};
4949
} else {
5050
if (this.nodeData.postRespVars) {
51-
const evalPostRespVars = computeNodeVariables(this.nodeData.postRespVars, res.data);
51+
const evalPostRespVars = computeNodeVariables(this.nodeData.postRespVars, response.data);
5252
this.logger.add(LogLevel.INFO, 'HTTP request success', {
5353
type: 'requestNode',
5454
data: {
55-
request: { type: options.method, url: options.url, data: options.data },
56-
response: res,
55+
request,
56+
response,
5757
preReqVars: evalVariables,
5858
postRespVars: evalPostRespVars,
5959
},
6060
});
6161
return {
6262
status: 'Success',
63-
data: res.data,
63+
data: response.data,
6464
postRespVars: evalPostRespVars,
6565
};
6666
}
6767
this.logger.add(LogLevel.INFO, 'HTTP request success', {
6868
type: 'requestNode',
6969
data: {
70-
request: { type: options.method, url: options.url, data: options.data },
71-
response: res,
70+
request,
71+
response,
7272
preReqVars: evalVariables,
7373
},
7474
});
7575
return {
7676
status: 'Success',
77-
data: res.data,
77+
data: response.data,
7878
};
7979
}
8080
}
8181

8282
async formulateRequest(finalUrl, variablesDict) {
8383
let restMethod = this.nodeData.requestType.toLowerCase();
84-
let contentType = 'application/json';
84+
let headers = {};
8585
let requestData = undefined;
8686

8787
if (this.nodeData.requestBody) {
8888
if (this.nodeData.requestBody.type === 'raw-json') {
89-
contentType = 'application/json';
89+
headers['content-type'] = 'application/json';
9090
requestData = this.nodeData.requestBody.body
9191
? JSON.parse(computeVariables(this.nodeData.requestBody.body, variablesDict))
9292
: JSON.parse('{}');
9393
} else if (this.nodeData.requestBody.type === 'form-data') {
94-
contentType = 'multipart/form-data';
94+
headers['content-type'] = 'multipart/form-data';
9595
const params = cloneDeep(this.nodeData.requestBody.body);
9696
requestData = params;
9797
}
@@ -100,9 +100,7 @@ class requestNode extends Node {
100100
const options = {
101101
method: restMethod,
102102
url: finalUrl,
103-
headers: {
104-
'content-type': contentType,
105-
},
103+
headers,
106104
data: requestData,
107105
};
108106

@@ -115,11 +113,11 @@ class requestNode extends Node {
115113
return options;
116114
}
117115

118-
runHttpRequest(request) {
116+
runHttpRequest(rawRequest) {
119117
const { ipcRenderer } = window;
120118

121119
return new Promise((resolve, reject) => {
122-
ipcRenderer.invoke('renderer:run-http-request', request, this.collectionPath).then(resolve).catch(reject);
120+
ipcRenderer.invoke('renderer:run-http-request', rawRequest, this.collectionPath).then(resolve).catch(reject);
123121
});
124122
}
125123
}

src/components/molecules/flow/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ const Flow = ({ tab, collectionId }) => {
219219
return (
220220
<div className='flex-auto'>
221221
<ReactFlow
222+
key={tab.id}
222223
nodes={nodes}
223224
edges={edges}
224225
onNodesChange={onNodesChange}

0 commit comments

Comments
 (0)