Skip to content

Commit 2c3940f

Browse files
authored
Merge pull request #120 from FlowTestAI/live-logs
feat: init user settings configuration
2 parents 40c7635 + a07dbea commit 2c3940f

30 files changed

Lines changed: 413 additions & 131 deletions

.changeset/shiny-chefs-double.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+
allow configurable user settings

packages/flowtest-cli/bin/index.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ const argv = yargs(hideBin(process.argv))
4545
})
4646
.option('timeout', {
4747
alias: 't',
48-
describe: 'timeout for graph run in ms',
48+
describe: 'timeout for flow run in ms',
4949
demandOption: false,
5050
type: 'number',
5151
})
5252
.option('scan', {
5353
alias: 's',
54-
describe: 'generate and upload build scan',
54+
describe: 'generate and upload flow scan',
5555
});
5656
},
5757
async (argv) => {
@@ -78,8 +78,8 @@ const argv = yargs(hideBin(process.argv))
7878
argv.env ? getEnvVariables(argv.env) : {},
7979
logger,
8080
);
81-
console.log(chalk.yellow('Running Graph \n'));
82-
if (flowData.nodes.find((n) => n.type === 'complexNode')) {
81+
console.log(chalk.yellow('Running Flow \n'));
82+
if (flowData.nodes.find((n) => n.type === 'flowNode')) {
8383
console.log(
8484
chalk.blue(
8585
'[Note] This flow contains nested flows so run it from parent directory of collection. Ignore if already doing that. \n',
@@ -89,9 +89,9 @@ const argv = yargs(hideBin(process.argv))
8989
const result = await g.run();
9090
console.log('\n');
9191
if (result.status === 'Success') {
92-
console.log(chalk.bold('Graph Run: ') + chalk.green(` ✓ `) + chalk.dim(result.status));
92+
console.log(chalk.bold('Flow Run: ') + chalk.green(` ✓ `) + chalk.dim(result.status));
9393
} else {
94-
console.log(chalk.bold('Graph Run: ') + chalk.red(` ✕ `) + chalk.dim(result.status));
94+
console.log(chalk.bold('Flow Run: ') + chalk.red(` ✕ `) + chalk.dim(result.status));
9595
}
9696
logger.add(LogLevel.INFO, `Total time: ${Date.now() - startTime} ms`);
9797
console.log(chalk.bold('Total Time: ') + chalk.dim(`${Date.now() - startTime} ms`));
@@ -108,18 +108,18 @@ const argv = yargs(hideBin(process.argv))
108108
'/upload',
109109
bytesToBase64(new TextEncoder().encode(JSON.stringify(data))),
110110
);
111-
console.log(chalk.bold('Build Scan: ') + chalk.dim(`${baseUrl}/scan/${response.data.data[0].id}`));
111+
console.log(chalk.bold('Flow Scan: ') + chalk.dim(`${baseUrl}/scan/${response.data.data[0].id}`));
112112
} catch (error) {
113113
if (error?.response) {
114-
if (error.response?.status === 403 || error.response?.status === 429) {
114+
if (error.response?.status >= 400 && error.response?.status < 500) {
115115
console.log(chalk.red(` ${JSON.stringify(error.response?.data)}`));
116116
}
117117

118118
if (error.response?.status === 500) {
119119
console.log(chalk.red(' Internal Server Error'));
120120
}
121121
}
122-
console.log(chalk.red(` ✕ `) + chalk.dim('Unable to upload build scan'));
122+
console.log(chalk.red(` ✕ `) + chalk.dim('Unable to upload flow scan'));
123123
}
124124
}
125125

packages/flowtest-cli/graph/Graph.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class Graph {
133133

134134
if (node.type === 'authNode') {
135135
console.log('Authentication Node');
136-
const aNode = new authNode(node.data, this.envVariables);
136+
const aNode = new authNode(node.data, this.envVariables, this.logger);
137137
this.auth = node.data.type ? aNode.evaluate() : undefined;
138138
result = {
139139
status: 'Success',

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const { computeVariables } = require('./utils');
22
const Node = require('./node');
33
const chalk = require('chalk');
4+
const { LogLevel } = require('../GraphLogger');
45

56
class authNode extends Node {
67
constructor(auth, envVariables, logger) {

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@ class requestNode extends Node {
5252
console.log(chalk.red(` ✕ `) + chalk.dim(`Request failed: ${JSON.stringify(res.error)}`));
5353
this.logger.add(LogLevel.ERROR, 'HTTP request failed', {
5454
type: 'requestNode',
55-
data: { request: { type: options.method, url: options.url }, response: res.error, preReqVars: evalVariables },
55+
data: {
56+
request: { type: options.method, url: options.url, data: options.data },
57+
response: res.error,
58+
preReqVars: evalVariables,
59+
},
5660
});
5761
return {
5862
status: 'Failed',
@@ -78,7 +82,11 @@ class requestNode extends Node {
7882
}
7983
this.logger.add(LogLevel.INFO, 'HTTP request success', {
8084
type: 'requestNode',
81-
data: { request: { type: options.method, url: options.url }, response: res, preReqVars: evalVariables },
85+
data: {
86+
request: { type: options.method, url: options.url, data: options.data },
87+
response: res,
88+
preReqVars: evalVariables,
89+
},
8290
});
8391
return {
8492
status: 'Success',

packages/flowtest-electron/electron-main.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const url = require('url');
55
const template = require('./electron-menu');
66
const Watcher = require('./src/app/watcher');
77
const registerRendererEventHandlers = require('./src/ipc/collection');
8+
const registerSettingsEventHandlers = require('./src/ipc/settings');
89

910
let mainWindow;
1011
let watcher;
@@ -59,6 +60,7 @@ app.on('ready', async () => {
5960
watcher = new Watcher();
6061

6162
registerRendererEventHandlers(mainWindow, watcher);
63+
registerSettingsEventHandlers(mainWindow);
6264
});
6365

6466
// Quit when all windows are closed, except on macOS. There, it's common

packages/flowtest-electron/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@smithy/signature-v4": "^3.0.0",
3939
"@smithy/util-utf8": "^3.0.0",
4040
"axios": "^1.6.7",
41+
"axios-retry": "^4.4.0",
4142
"chokidar": "^3.6.0",
4243
"dotenv": "^16.4.5",
4344
"electron-store": "^8.1.0",
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// lib/axiosClient.ts
2+
const axios = require('axios');
3+
const axiosRetry = require('axios-retry').default;
4+
5+
const axiosClient = (baseUrl, accessId, accessKey) => {
6+
const client = axios.create({
7+
baseURL: `${baseUrl}/api`,
8+
headers: {
9+
'Content-Type': 'application/json',
10+
},
11+
});
12+
13+
axiosRetry(client, {
14+
retries: 3, // Number of retries
15+
retryDelay: (retryCount) => {
16+
return retryCount * 1000; // Time interval between retries (1000 ms = 1 second)
17+
},
18+
retryCondition: (error) => {
19+
// Retry on network errors or rate limit errors or 5xx server errors
20+
return error.response?.status === 500 || error.response?.status === 429 || error.code === 'ECONNABORTED';
21+
},
22+
});
23+
24+
return client;
25+
};
26+
27+
module.exports = {
28+
axiosClient,
29+
};

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

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const readFile = require('../utils/filemanager/readfile');
1717
const FlowtestAI = require('../ai/flowtestai');
1818
const { stringify, parse } = require('flatted');
1919
const { deserialize, serialize } = require('../utils/flowparser/parser');
20+
const { axiosClient } = require('./axiosClient');
2021

2122
const collectionStore = new Collections();
2223
const flowTestAI = new FlowtestAI();
@@ -317,7 +318,9 @@ const registerRendererEventHandlers = (mainWindow, watcher) => {
317318
} else {
318319
return {
319320
error: {
320-
message: `An error occurred while running the request : ${error?.message}`,
321+
status: '',
322+
statusText: '',
323+
data: `An error occurred while running the request : ${error?.message}`,
321324
},
322325
};
323326
}
@@ -336,6 +339,55 @@ const registerRendererEventHandlers = (mainWindow, watcher) => {
336339
return Promise.reject(error);
337340
}
338341
});
342+
343+
ipcMain.handle('renderer:upload-logs', async (event, name, config, logs) => {
344+
function bytesToBase64(bytes) {
345+
const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join('');
346+
return btoa(binString);
347+
}
348+
349+
try {
350+
const data = {
351+
version: 1,
352+
name,
353+
scan: logs,
354+
};
355+
try {
356+
const response = await axiosClient(config.hostUrl, config.accessId, config.accessKey).post(
357+
'/upload',
358+
bytesToBase64(new TextEncoder().encode(JSON.stringify(data))),
359+
);
360+
return {
361+
upload: 'success',
362+
url: `${config.hostUrl}/scan/${response.data.data[0].id}`,
363+
};
364+
} catch (error) {
365+
if (error?.response) {
366+
if (error.response?.status >= 400 && error.response?.status < 500) {
367+
return {
368+
upload: 'fail',
369+
message: 'Unable to upload flow scan',
370+
reason: `${JSON.stringify(error.response?.data)}`,
371+
};
372+
}
373+
374+
if (error.response?.status === 500) {
375+
return {
376+
upload: 'fail',
377+
message: 'Unable to upload flow scan',
378+
reason: 'Internal Server Error',
379+
};
380+
}
381+
}
382+
return {
383+
upload: 'fail',
384+
message: 'Unable to upload flow scan',
385+
};
386+
}
387+
} catch (error) {
388+
return Promise.reject(error);
389+
}
390+
});
339391
};
340392

341393
module.exports = registerRendererEventHandlers;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const { ipcMain, shell, dialog, app } = require('electron');
2+
const Settings = require('../store/settings');
3+
4+
const settingsStore = new Settings();
5+
6+
const registerSettingsEventHandlers = (mainWindow) => {
7+
ipcMain.handle('renderer:settings-window-ready', async (event) => {
8+
const savedSettings = settingsStore.getAll();
9+
10+
mainWindow.webContents.send('main:saved-settings', savedSettings);
11+
});
12+
13+
ipcMain.handle('renderer:add-logsyncconfig', async (event, config) => {
14+
try {
15+
settingsStore.addLogSyncConfig(...config);
16+
const savedSettings = settingsStore.getAll();
17+
18+
mainWindow.webContents.send('main:saved-settings', savedSettings);
19+
} catch (error) {
20+
return Promise.reject(error);
21+
}
22+
});
23+
};
24+
25+
module.exports = registerSettingsEventHandlers;

0 commit comments

Comments
 (0)