Skip to content

Commit 5c0187b

Browse files
committed
properly read file data in multipart form request type
1 parent 8150834 commit 5c0187b

9 files changed

Lines changed: 86 additions & 54 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"date-fns": "^3.6.0",
5757
"eslint-import-resolver-alias": "^1.1.2",
5858
"eslint-plugin-import": "^2.29.1",
59+
"form-data": "^4.0.0",
5960
"immer": "^10.0.4",
6061
"lodash": "^4.17.21",
6162
"mousetrap": "^1.6.5",

packages/flowtest-cli/bin/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const argv = yargs(hideBin(process.argv))
8383
if (flowData.nodes.find((n) => n.type === 'flowNode')) {
8484
console.log(
8585
chalk.blue(
86-
'[Note] This flow contains nested flows so run it from parent directory of collection. Ignore if already doing that. \n',
86+
'[Note] This flow contains nested flows so run it from root directory of collection. Ignore if already doing that. \n',
8787
),
8888
);
8989
}

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ const FlowtestAI = require('../ai/flowtestai');
1818
const { stringify, parse } = require('flatted');
1919
const { deserialize, serialize } = require('../utils/flowparser/parser');
2020
const { axiosClient } = require('./axiosClient');
21+
const FormData = require('form-data');
22+
const { extend } = require('lodash');
2123

2224
const collectionStore = new Collections();
2325
const flowTestAI = new FlowtestAI();
@@ -283,23 +285,38 @@ const registerRendererEventHandlers = (mainWindow, watcher) => {
283285
}
284286
});
285287

286-
ipcMain.handle('renderer:run-http-request', async (event, request) => {
288+
ipcMain.handle('renderer:run-http-request', async (event, request, collectionPath) => {
287289
try {
288-
if (request.headers['Content-type'] === 'multipart/form-data') {
289-
const requestData = new FormData();
290-
const file = await convertBase64ToBlob(request.data.value);
291-
requestData.append(request.data.key, file, request.data.name);
290+
if (request.headers['content-type'] === 'multipart/form-data') {
291+
const formData = new FormData();
292+
const params = request.data;
293+
await params.map(async (param, index) => {
294+
if (param.type === 'text') {
295+
formData.append(param.key, param.value);
296+
}
297+
298+
if (param.type === 'file') {
299+
let trimmedFilePath = param.value.trim();
300+
301+
if (!path.isAbsolute(trimmedFilePath)) {
302+
trimmedFilePath = path.join(collectionPath, trimmedFilePath);
303+
}
292304

293-
request.data = requestData;
305+
formData.append(param.key, fs.createReadStream(trimmedFilePath), path.basename(trimmedFilePath));
306+
}
307+
});
308+
309+
request.data = formData;
310+
extend(request.headers, formData.getHeaders());
294311
}
295312

296-
// assuming 'application/json' type
297313
const options = {
298314
...request,
299315
signal: newAbortSignal(),
300316
};
301317

302318
const result = await axios(options);
319+
303320
return {
304321
status: result.status,
305322
statusText: result.statusText,

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/molecules/flow/graph/Graph.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import setVarNode from './compute/setvarnode';
1111
import { LogLevel } from './GraphLogger';
1212

1313
class Graph {
14-
constructor(nodes, edges, startTime, initialEnvVars, logger, caller) {
14+
constructor(nodes, edges, startTime, initialEnvVars, logger, caller, collectionPath) {
1515
this.nodes = nodes;
1616
this.edges = edges;
1717
this.logger = logger;
@@ -21,6 +21,7 @@ class Graph {
2121
this.auth = undefined;
2222
this.envVariables = initialEnvVars;
2323
this.caller = caller;
24+
this.collectionPath = collectionPath;
2425
}
2526

2627
#checkTimeout() {
@@ -125,7 +126,14 @@ class Graph {
125126
}
126127

127128
if (node.type === 'requestNode') {
128-
const rNode = new requestNode(node.data, prevNodeOutputData, this.envVariables, this.auth, this.logger);
129+
const rNode = new requestNode(
130+
node.data,
131+
prevNodeOutputData,
132+
this.envVariables,
133+
this.auth,
134+
this.logger,
135+
this.collectionPath,
136+
);
129137
result = await rNode.evaluate();
130138
// add post response variables if any
131139
if (result.postRespVars) {
@@ -146,6 +154,7 @@ class Graph {
146154
this.envVariables,
147155
this.logger,
148156
node.type,
157+
this.collectionPath,
149158
);
150159
result = await cNode.evaluate();
151160
this.envVariables = result.envVars;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import Graph1 from '../Graph';
22
import Node from './node';
33

44
class nestedFlowNode extends Node {
5-
constructor(nodes, edges, startTime, initialEnvVars, logger, caller) {
5+
constructor(nodes, edges, startTime, initialEnvVars, logger, caller, collectionPath) {
66
super('flowNode');
7-
this.internalGraph = new Graph1(nodes, edges, startTime, initialEnvVars, logger, caller);
7+
this.internalGraph = new Graph1(nodes, edges, startTime, initialEnvVars, logger, caller, collectionPath);
88
}
99

1010
async evaluate() {

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

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1+
import { cloneDeep } from 'lodash';
12
import { computeNodeVariables, computeVariables } from '../compute/utils';
23
import { LogLevel } from '../GraphLogger';
34
import Node from './node';
45

56
class requestNode extends Node {
6-
constructor(nodeData, prevNodeOutputData, envVariables, auth, logger) {
7+
constructor(nodeData, prevNodeOutputData, envVariables, auth, logger, collectionPath) {
78
super('requestNode');
89
this.nodeData = nodeData;
910
this.prevNodeOutputData = prevNodeOutputData;
1011
this.envVariables = envVariables;
1112
this.auth = auth;
1213
this.logger = logger;
14+
this.collectionPath = collectionPath;
1315
}
1416

1517
async evaluate() {
@@ -28,14 +30,10 @@ class requestNode extends Node {
2830
console.debug('Evaluated Url: ', finalUrl);
2931

3032
// step 3
31-
const options = this.formulateRequest(finalUrl, variablesDict);
33+
const options = await this.formulateRequest(finalUrl, variablesDict);
3234

3335
const res = await this.runHttpRequest(options);
3436

35-
if (this.nodeData?.requestBody?.type === 'form-data') {
36-
options.data.value = '<BASE64_ENCODED_FILE_DATA>';
37-
}
38-
3937
if (res.error) {
4038
this.logger.add(LogLevel.ERROR, 'HTTP request failed', {
4139
type: 'requestNode',
@@ -81,7 +79,7 @@ class requestNode extends Node {
8179
}
8280
}
8381

84-
formulateRequest(finalUrl, variablesDict) {
82+
async formulateRequest(finalUrl, variablesDict) {
8583
let restMethod = this.nodeData.requestType.toLowerCase();
8684
let contentType = 'application/json';
8785
let requestData = undefined;
@@ -94,19 +92,16 @@ class requestNode extends Node {
9492
: JSON.parse('{}');
9593
} else if (this.nodeData.requestBody.type === 'form-data') {
9694
contentType = 'multipart/form-data';
97-
requestData = {
98-
key: computeVariables(this.nodeData.requestBody.body.key, variablesDict),
99-
value: this.nodeData.requestBody.body.value,
100-
name: this.nodeData.requestBody.body.name,
101-
};
95+
const params = cloneDeep(this.nodeData.requestBody.body);
96+
requestData = params;
10297
}
10398
}
10499

105100
const options = {
106101
method: restMethod,
107102
url: finalUrl,
108103
headers: {
109-
'Content-type': contentType,
104+
'content-type': contentType,
110105
},
111106
data: requestData,
112107
};
@@ -124,7 +119,7 @@ class requestNode extends Node {
124119
const { ipcRenderer } = window;
125120

126121
return new Promise((resolve, reject) => {
127-
ipcRenderer.invoke('renderer:run-http-request', request).then(resolve).catch(reject);
122+
ipcRenderer.invoke('renderer:run-http-request', request, this.collectionPath).then(resolve).catch(reject);
128123
});
129124
}
130125
}

src/components/molecules/flow/index.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ const Flow = ({ tab, collectionId }) => {
243243
>
244244
<Background variant='dots' gap={12} size={1} />
245245
<Controls
246-
className='flex shadow-none border-cyan-900'
246+
className='flex border-cyan-900 shadow-none'
247247
onFitView={() => setViewport(reactFlowInstance.getViewport())}
248248
></Controls>
249249
<Button
@@ -257,10 +257,10 @@ const Flow = ({ tab, collectionId }) => {
257257
try {
258258
let envVariables = {};
259259

260-
const activeEnv = useCollectionStore
261-
.getState()
262-
.collections.find((c) => c.id === collectionId)
263-
?.environments.find((e) => e.name === useTabStore.getState().selectedEnv);
260+
const activeCollection = useCollectionStore.getState().collections.find((c) => c.id === collectionId);
261+
const activeEnv = activeCollection?.environments.find(
262+
(e) => e.name === useTabStore.getState().selectedEnv,
263+
);
264264
if (activeEnv) {
265265
envVariables = cloneDeep(activeEnv.variables);
266266
}
@@ -273,6 +273,7 @@ const Flow = ({ tab, collectionId }) => {
273273
envVariables,
274274
logger,
275275
'main',
276+
activeCollection.pathname,
276277
);
277278
const result = await g.run();
278279
const time = Date.now() - startTime;

0 commit comments

Comments
 (0)