Skip to content

Commit 3abf6b3

Browse files
committed
basic layout and communication setup for user settings
1 parent 06fcef6 commit 3abf6b3

13 files changed

Lines changed: 225 additions & 37 deletions

File tree

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: 50 additions & 0 deletions
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();
@@ -336,6 +337,55 @@ const registerRendererEventHandlers = (mainWindow, watcher) => {
336337
return Promise.reject(error);
337338
}
338339
});
340+
341+
ipcMain.handle('renderer:upload-logs', async (event, name, config, logs) => {
342+
function bytesToBase64(bytes) {
343+
const binString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join('');
344+
return btoa(binString);
345+
}
346+
347+
try {
348+
const data = {
349+
version: 1,
350+
name,
351+
scan: logs,
352+
};
353+
try {
354+
const response = await axiosClient(config.hostUrl, config.accessId, config.accessKey).post(
355+
'/upload',
356+
bytesToBase64(new TextEncoder().encode(JSON.stringify(data))),
357+
);
358+
return {
359+
upload: 'success',
360+
url: `${config.hostUrl}/scan/${response.data.data[0].id}`,
361+
};
362+
} catch (error) {
363+
if (error?.response) {
364+
if (error.response?.status === 403 || error.response?.status === 429) {
365+
return {
366+
upload: 'fail',
367+
message: 'Unable to upload build scan',
368+
reason: `${JSON.stringify(error.response?.data)}`,
369+
};
370+
}
371+
372+
if (error.response?.status === 500) {
373+
return {
374+
upload: 'fail',
375+
message: 'Unable to upload build scan',
376+
reason: 'Internal Server Error',
377+
};
378+
}
379+
}
380+
return {
381+
upload: 'fail',
382+
message: 'Unable to upload build scan',
383+
};
384+
}
385+
} catch (error) {
386+
return Promise.reject(error);
387+
}
388+
});
339389
};
340390

341391
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;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const Store = require('electron-store');
2+
3+
class Settings {
4+
constructor() {
5+
this.store = new Store();
6+
}
7+
8+
addLogSyncConfig(enabled, hostUrl, accessId, accessKey) {
9+
this.store.set('logSyncConfig', { enabled, hostUrl, accessId, accessKey });
10+
}
11+
12+
getAll() {
13+
return {
14+
logSyncConfig: this.store.get('logSyncConfig') || {},
15+
};
16+
}
17+
18+
clearAll() {
19+
this.store.set('logSyncConfig', {});
20+
}
21+
}
22+
23+
module.exports = Settings;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const Settings = require('../../src/store/settings');
2+
3+
describe('settings-store', () => {
4+
it('should create and get settings', async () => {
5+
const store = new Settings();
6+
store.clearAll();
7+
8+
expect(store.getAll().logSyncConfig).toEqual({});
9+
10+
// adding a collection whose directory doesn't exist
11+
store.addLogSyncConfig(true, 'http://localhost:3000', 'access_id', 'access_key');
12+
const config = store.getAll().logSyncConfig;
13+
expect(config.enabled).toEqual(true);
14+
expect(config.hostUrl).toEqual('http://localhost:3000');
15+
expect(config.accessId).toEqual('access_id');
16+
expect(config.accessKey).toEqual('access_key');
17+
});
18+
});

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/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import Button from 'components/atoms/common/Button';
2727
import { BUTTON_INTENT_TYPES, BUTTON_TYPES } from 'constants/Common';
2828
import GraphLogger, { LogLevel } from './graph/GraphLogger';
2929
import Mousetrap from 'mousetrap';
30+
import useSettingsStore from 'stores/SettingsStore';
3031

3132
const StartNode = () => (
3233
<FlowNode title='Start' handleLeft={false} handleRight={true} handleRightData={{ type: 'source' }}></FlowNode>
@@ -87,6 +88,7 @@ const Flow = ({ tab, collectionId }) => {
8788
useCanvasStore(selector);
8889

8990
const setLogs = useTabStore((state) => state.updateFlowTestLogs);
91+
const logSyncConfig = useSettingsStore((state) => state.logSyncConfig);
9092

9193
const [reactFlowInstance, setReactFlowInstance] = useState(null);
9294

@@ -196,9 +198,9 @@ const Flow = ({ tab, collectionId }) => {
196198
const onGraphComplete = (status, logs) => {
197199
setLogs(tab.id, logs);
198200
if (status == 'Success') {
199-
toast.success(`FlowTest Run Success! \n View Logs`);
201+
toast.success(`FlowTest Run Success!`);
200202
} else if (status == 'Failed') {
201-
toast.error(`FlowTest Run Failed! \n View Logs`);
203+
toast.error(`FlowTest Run Failed!`);
202204
}
203205
runnableEdges(false);
204206
};

0 commit comments

Comments
 (0)