Skip to content

Commit 0675e6e

Browse files
authored
Feat: backup to csv (#137)
Feat: backup to csv - generate headers of csv from index mapping - transform json data to csv Refs: #23 --------- Signed-off-by: seven <[email protected]>
1 parent 4f1a76d commit 0675e6e

File tree

9 files changed

+159
-79
lines changed

9 files changed

+159
-79
lines changed

.eslintrc.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@ module.exports = {
4040
'no-console': 'error',
4141
'no-debugger': process.env === 'development' ? 'warn' : 'error',
4242
},
43-
ignorePatterns: ['dist','src-tauri'],
43+
ignorePatterns: ['dist', 'src-tauri', 'coverage'],
4444
};

jest.config.cjs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/** @type {import('ts-jest').JestConfigWithTsJest} */
21
module.exports = {
32
preset: 'ts-jest',
43
testEnvironment: 'node',

package-lock.json

+90-41
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@
4343
"devDependencies": {
4444
"@tauri-apps/cli": "^1",
4545
"@types/debug": "^4.1.12",
46-
"@types/jest": "^29.5.12",
47-
"@types/markdown-it": "^14.1.1",
46+
"@types/jest": "^29.5.14",
4847
"@types/lodash": "^4.17.12",
48+
"@types/markdown-it": "^14.1.1",
4949
"@typescript-eslint/eslint-plugin": "^7.14.1",
5050
"@typescript-eslint/parser": "^7.14.1",
5151
"@vicons/antd": "^0.12.0",
@@ -62,7 +62,7 @@
6262
"naive-ui": "^2.38.2",
6363
"prettier": "^3.3.2",
6464
"sass": "^1.77.6",
65-
"ts-jest": "^29.1.5",
65+
"ts-jest": "^29.2.5",
6666
"typescript": "^5.5.2",
6767
"unplugin-auto-import": "^0.17.6",
6868
"unplugin-vue-components": "^0.27.1",

src/store/backupRestoreStore.ts

+32-25
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@ export const useBackupRestoreStore = defineStore('backupRestoreStore', {
5555
const filePath = `${input.backupFolder}/${input.backupFileName}.${input.backupFileType}`;
5656
let searchAfter: any[] | undefined = undefined;
5757
let hasMore = true;
58+
let appendFile = false;
5859

5960
try {
6061
this.backupProgress = {
6162
complete: 0,
6263
total: (await client.get(`/${input.index}/_count`)).count,
6364
};
65+
const { [input.index]: backupIndexMapping } = await client.get(`/${input.index}/_mapping`);
66+
const csvHeaders = buildCsvHeaders(backupIndexMapping);
67+
let dataToWrite = input.backupFileType === 'json' ? '' : csvHeaders.join(',') + '\r\n';
6468

6569
while (hasMore) {
6670
const response = await client.get(
@@ -90,11 +94,13 @@ export const useBackupRestoreStore = defineStore('backupRestoreStore', {
9094
hasMore = false;
9195
} else {
9296
searchAfter = hits[hits.length - 1].sort;
93-
const dataToWrite =
97+
dataToWrite +=
9498
input.backupFileType === 'json'
9599
? JSON.stringify(hits)
96-
: JSON.stringify(convertToCsv(hits));
97-
await sourceFileApi.saveFile(filePath, dataToWrite, true);
100+
: convertToCsv(csvHeaders, hits);
101+
await sourceFileApi.saveFile(filePath, dataToWrite, appendFile);
102+
dataToWrite = '';
103+
appendFile = true;
98104
}
99105
}
100106
return filePath;
@@ -108,28 +114,29 @@ export const useBackupRestoreStore = defineStore('backupRestoreStore', {
108114
},
109115
});
110116

111-
const flattenObject = (obj: any, parent: string = '', res: any = {}) => {
112-
for (let key in obj) {
113-
const propName = parent ? `${parent}.${key}` : key;
114-
if (typeof obj[key] === 'object' && obj[key] !== null) {
115-
flattenObject(obj[key], propName, res);
116-
} else {
117-
res[propName] = obj[key];
118-
}
119-
}
120-
return res;
117+
const buildCsvHeaders = ({
118+
mappings,
119+
}: {
120+
mappings: {
121+
properties: {
122+
[key: string]: unknown;
123+
};
124+
};
125+
}) => {
126+
return Object.keys(mappings.properties);
121127
};
122128

123-
const convertToCsv = (data: any[]) => {
124-
if (data.length === 0) {
125-
return { headers: [], data: [] };
126-
}
127-
128-
const flattenedData = data.map(row => flattenObject(row));
129-
const headers = Array.from(new Set(flattenedData.flatMap(row => Object.keys(row))));
130-
const csvRows = flattenedData.map(row =>
131-
headers.map(header => JSON.stringify(row[header] ?? '')).join(','),
132-
);
133-
134-
return { headers, data: csvRows };
129+
const convertToCsv = (headers: Array<string>, data: unknown[]) => {
130+
return data
131+
.map(item =>
132+
headers
133+
.map(header => {
134+
const data = get(item, `_source.${header}`, null);
135+
return data === null || !['object', 'symbol', 'function'].includes(typeof data)
136+
? data
137+
: JSON.stringify(data);
138+
})
139+
.join(','),
140+
)
141+
.join('\r\n');
135142
};

src/views/backup-restore/components/backup.vue

+8-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,14 @@ const submitBackup = async () => {
305305
positiveText: lang.t('dialogOps.confirm'),
306306
negativeText: lang.t('dialogOps.cancel'),
307307
onPositiveClick: async () => {
308-
await saveBackup(backupInput);
308+
saveBackup(backupInput).catch(err => {
309+
const error = err as CustomError;
310+
message.error(`status: ${error.status}, details: ${error.details}`, {
311+
closable: true,
312+
keepAliveOnHover: true,
313+
duration: 3600,
314+
});
315+
});
309316
},
310317
onNegativeClick: () => {},
311318
});

tests/fixtures/index.ts

Whitespace-only changes.

0 commit comments

Comments
 (0)