@devassure/tpp-engine 0.0.0-stage → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEV_README.md +19 -0
- package/README.md +6 -3
- package/index.js +121 -0
- package/package.json +30 -4
- package/src/accessibility/combined-a11y-report.js +208 -0
- package/src/accessibility/tpp-a11y.js +81 -0
- package/src/actions.js +3201 -0
- package/src/api/api-auth.js +18 -0
- package/src/api/api-service.js +289 -0
- package/src/app-config.json +7 -0
- package/src/autoheal-actions.js +309 -0
- package/src/console-log/console-log-manager.js +39 -0
- package/src/console-log/console-log-utils.js +201 -0
- package/src/database/mysql/mysql-service.js +23 -0
- package/src/database/neo4j/neo4j-service.js +32 -0
- package/src/database/oracle/oracle-service.js +48 -0
- package/src/database/pg/pg-service.js +29 -0
- package/src/downloads/downloads-manager.js +86 -0
- package/src/downloads/downloads-utils.js +113 -0
- package/src/globalsetup.js +68 -0
- package/src/globalteardown.js +57 -0
- package/src/hooks/each-hook.js +45 -0
- package/src/hooks/hook-utils.js +21 -0
- package/src/library/authenticator/authenticator-utils.js +43 -0
- package/src/library/csv/csv-utils.js +297 -0
- package/src/library/date/date-utils.js +168 -0
- package/src/library/emailTesting/TokenConfig.js +46 -0
- package/src/library/emailTesting/emailTesting-utils.js +527 -0
- package/src/library/excel/excel-utils.js +922 -0
- package/src/library/file/file-action-utils.js +129 -0
- package/src/library/file/file-utils.js +127 -0
- package/src/library/json/json-utils.js +104 -0
- package/src/library/math/math-utils.js +283 -0
- package/src/library/softAssert/softAssert-utils.js +154 -0
- package/src/library/table/table-utils.js +92 -0
- package/src/library/text/text-utils.js +45 -0
- package/src/library/tpp-library.js +81 -0
- package/src/library/web/slider-utils.js +66 -0
- package/src/mobile/appium/appium-manager.js +158 -0
- package/src/mobile/device-cloud/browserstack-service.js +44 -0
- package/src/mobile/device-cloud/commands/browserstack-commands.js +20 -0
- package/src/mobile/device-cloud/commands/device-cloud-commands.js +17 -0
- package/src/mobile/device-cloud/device-cloud-service.js +29 -0
- package/src/mobile/device-cloud/pcloudy-service.js +65 -0
- package/src/mobile/mobile-actions.js +1568 -0
- package/src/mobile/recorder-utils/css-selector-generator.js +2 -0
- package/src/mobile/recorder-utils/finder.js +752 -0
- package/src/mobile/recorder-utils/mobile-recorder.js +266 -0
- package/src/mobile/web-driver-utils.js +110 -0
- package/src/models/tpp-element.js +58 -0
- package/src/models/tpp-mobile-element.js +50 -0
- package/src/network-log/network-log-manager.js +96 -0
- package/src/network-log/network-log-utils.js +361 -0
- package/src/types/elements.types.js +24 -0
- package/src/utils/ai-utils.js +48 -0
- package/src/utils/assert-utils.js +50 -0
- package/src/utils/autoheal-utils.js +9 -0
- package/src/utils/common-utils.js +120 -0
- package/src/utils/cookie-utils.js +70 -0
- package/src/utils/json-utils.js +10 -0
- package/src/utils/log-utils.js +10 -0
- package/src/utils/logger.js +76 -0
- package/src/utils/screenshot-utils.js +42 -0
- package/src/utils/token-utils.js +18 -0
- package/src/utils/vectordb-utils.js +125 -0
- package/src/visual-test/tpp-visual-test.js +122 -0
- package/src/web/web-executor-agent.js +149 -0
- package/src/web/web-executor-data-fetch.js +353 -0
- package/test-excel-utils.js +570 -0
|
@@ -0,0 +1,922 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const Excel = require('exceljs');
|
|
4
|
+
const xlsx = require('xlsx');
|
|
5
|
+
|
|
6
|
+
const { Logger } = require('../../utils/logger');
|
|
7
|
+
const { FileUtils } = require('../file/file-utils');
|
|
8
|
+
const { TableUtils } = require('../table/table-utils');
|
|
9
|
+
const { processExcel } = require('../../utils/vectordb-utils');
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
getLastElements,
|
|
13
|
+
getElementsInRange,
|
|
14
|
+
getFirstElements,
|
|
15
|
+
getElementsAtIndices,
|
|
16
|
+
getAnyElements,
|
|
17
|
+
sleep,
|
|
18
|
+
getBoolean,
|
|
19
|
+
isNumber
|
|
20
|
+
} = require('../../utils/common-utils');
|
|
21
|
+
|
|
22
|
+
class ExcelUtils {
|
|
23
|
+
constructor(caller, blacklistedLogs, customConfig) {
|
|
24
|
+
this.caller = caller;
|
|
25
|
+
this.blacklistedLogs = blacklistedLogs;
|
|
26
|
+
this.customConfig = customConfig;
|
|
27
|
+
this.fileUtils = new FileUtils();
|
|
28
|
+
this.dependenciesPath = customConfig?.dependenciesPath;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setCaller(caller) {
|
|
32
|
+
this.caller = caller;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setBlacklistedLogs(blacklistedLogs) {
|
|
36
|
+
this.blacklistedLogs = blacklistedLogs;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ---------- Small internal helpers ----------
|
|
40
|
+
|
|
41
|
+
_getLogger(logLabel) {
|
|
42
|
+
return new Logger(this.caller, this.blacklistedLogs);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
_ensureFileExists(rawPath, logger, logLabel) {
|
|
46
|
+
const filePath = this.fileUtils.getFilePath(rawPath);
|
|
47
|
+
if (!fs.existsSync(filePath)) {
|
|
48
|
+
logger.failureLog(logLabel);
|
|
49
|
+
throw { message: `Excel file ${filePath} does not exists!` };
|
|
50
|
+
}
|
|
51
|
+
return filePath;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_cleanupTempFile(filePath) {
|
|
55
|
+
if (!filePath) return;
|
|
56
|
+
try {
|
|
57
|
+
if (fs.existsSync(filePath)) {
|
|
58
|
+
fs.unlinkSync(filePath);
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// ignore cleanup errors
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_isSheetsReadError(err) {
|
|
66
|
+
return err?.message?.includes("reading 'sheets'");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async _fixFileWithXlsxAndSleep(filePath, logLabel, logger, sleepMs = 1000) {
|
|
70
|
+
const fixed = await this.fixExcelFileWithXlsx(filePath, logLabel, logger);
|
|
71
|
+
await sleep(sleepMs);
|
|
72
|
+
return fixed;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_compareAndLogRowDifference(row1, row2, rowNumber, sheetName, allSheets, logLabel, logger, failures) {
|
|
76
|
+
if (JSON.stringify(row1) !== JSON.stringify(row2)) {
|
|
77
|
+
let message =
|
|
78
|
+
!row1?.length
|
|
79
|
+
? `Row ${rowNumber} is empty in first file`
|
|
80
|
+
: !row2?.length
|
|
81
|
+
? `Row ${rowNumber} is empty in second file`
|
|
82
|
+
: `❌ Row ${rowNumber} is different!`;
|
|
83
|
+
|
|
84
|
+
if (allSheets && sheetName) {
|
|
85
|
+
message = `${sheetName}: ${message}`;
|
|
86
|
+
}
|
|
87
|
+
failures.push(message);
|
|
88
|
+
logger.log(logLabel, message);
|
|
89
|
+
|
|
90
|
+
if (row1?.length && row2?.length) {
|
|
91
|
+
logger.log(logLabel, `row in 1st File: ${JSON.stringify(row1)}`);
|
|
92
|
+
logger.log(logLabel, `row in 2nd File: ${JSON.stringify(row2)}`);
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ---------- Public wrappers (unchanged behavior) ----------
|
|
100
|
+
|
|
101
|
+
async compare_all_sheets(filePath1, filePath2) {
|
|
102
|
+
const logLabel = `Excel.Compare data of all sheets of ${filePath1} and ${filePath2}`;
|
|
103
|
+
const logger = this._getLogger(logLabel);
|
|
104
|
+
logger.startLog(logLabel);
|
|
105
|
+
await this.compareExcelFiles(filePath1, filePath2, undefined, undefined, true, logLabel);
|
|
106
|
+
logger.endLog(logLabel);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async compare_first_sheet(filePath1, filePath2) {
|
|
110
|
+
const logLabel = `Excel.Compare data of first sheet of ${filePath1} and ${filePath2}`;
|
|
111
|
+
const logger = this._getLogger(logLabel);
|
|
112
|
+
logger.startLog(logLabel);
|
|
113
|
+
await this.compareExcelFiles(filePath1, filePath2, undefined, undefined, false, logLabel);
|
|
114
|
+
logger.endLog(logLabel);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async compare_given_sheet(filePath1, sheetName1, filePath2, sheetName2) {
|
|
118
|
+
const logLabel = `Excel.Compare data of file ${filePath1} sheet ${sheetName1} and file ${filePath2} sheet ${sheetName2}`;
|
|
119
|
+
const logger = this._getLogger(logLabel);
|
|
120
|
+
logger.startLog(logLabel);
|
|
121
|
+
await this.compareExcelFiles(filePath1, filePath2, sheetName1, sheetName2, false, logLabel);
|
|
122
|
+
logger.endLog(logLabel);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async read_first_sheet_rows_as_array(filePath) {
|
|
126
|
+
return await this.read_rows_as_array(filePath);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async read_rows_as_array(filePath, sheetName) {
|
|
130
|
+
const logLabel = sheetName
|
|
131
|
+
? `Excel.Read sheet ${sheetName} rows as array ${filePath}`
|
|
132
|
+
: `Excel.Read first sheet rows as array ${filePath}`;
|
|
133
|
+
|
|
134
|
+
const logger = this._getLogger(logLabel);
|
|
135
|
+
logger.startLog(logLabel);
|
|
136
|
+
const data = await this.readSheetAsArray(filePath, sheetName, logLabel);
|
|
137
|
+
logger.endLog(logLabel);
|
|
138
|
+
return data;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async read_first_sheet_rows_as_object(filePath) {
|
|
142
|
+
return await this.read_rows_as_object(filePath);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async read_rows_as_object(filePath, sheetName) {
|
|
146
|
+
const logLabel = sheetName
|
|
147
|
+
? `Excel.Read sheet ${sheetName} rows as objects ${filePath}`
|
|
148
|
+
: `Excel.Read first sheet rows as objects ${filePath}`;
|
|
149
|
+
|
|
150
|
+
const logger = this._getLogger(logLabel);
|
|
151
|
+
logger.startLog(logLabel);
|
|
152
|
+
|
|
153
|
+
const dataArray = await this.readSheetAsArray(filePath, sheetName, logLabel);
|
|
154
|
+
const fields = dataArray[0] || [];
|
|
155
|
+
const data = dataArray.slice(1).map(cells => {
|
|
156
|
+
const row = {};
|
|
157
|
+
for (let i = 0; i < fields.length; i++) {
|
|
158
|
+
if (!fields[i]) continue;
|
|
159
|
+
row[`${fields[i]}`] = i < cells.length ? cells[i] || '' : '';
|
|
160
|
+
}
|
|
161
|
+
return row;
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
logger.endLog(logLabel);
|
|
165
|
+
return data;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ---------- Core reading helpers ----------
|
|
169
|
+
|
|
170
|
+
async readFixedFileAsArray(filePath, sheetName, logLabel, rowCount, ignoreFirstRow) {
|
|
171
|
+
const workbook = new Excel.Workbook();
|
|
172
|
+
await workbook.xlsx.readFile(filePath);
|
|
173
|
+
|
|
174
|
+
let sheet = sheetName
|
|
175
|
+
? workbook.getWorksheet(sheetName)
|
|
176
|
+
: workbook.worksheets[0];
|
|
177
|
+
|
|
178
|
+
if (!sheet) {
|
|
179
|
+
const msg = sheetName
|
|
180
|
+
? `Sheet with name ${sheetName} not found!`
|
|
181
|
+
: 'No sheet found in the file!';
|
|
182
|
+
throw { message: msg };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const result = [];
|
|
186
|
+
const maxRow = rowCount ? Math.min(rowCount, sheet.rowCount) : sheet.rowCount;
|
|
187
|
+
|
|
188
|
+
for (let i = 1; i <= maxRow; i++) {
|
|
189
|
+
const row = sheet.getRow(i);
|
|
190
|
+
if (!row || row.values.length <= 1) continue;
|
|
191
|
+
const cleanedRow = row.values.slice(1); // first index unused
|
|
192
|
+
result.push(cleanedRow);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (ignoreFirstRow && result.length > 0) {
|
|
196
|
+
return result.slice(1);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async fixExcelFileWithXlsx(filePath, logLabel, loggerFromCaller) {
|
|
203
|
+
const logger = loggerFromCaller || this._getLogger(logLabel);
|
|
204
|
+
logger.log(
|
|
205
|
+
logLabel,
|
|
206
|
+
'Excel file appears corrupted or incompatible with ExcelJS stream reader. Attempting to fix using xlsx library...'
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const workbook = xlsx.readFile(filePath);
|
|
211
|
+
|
|
212
|
+
if (!workbook.SheetNames || workbook.SheetNames.length === 0) {
|
|
213
|
+
throw { message: 'No sheets found in the Excel file' };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const newWorkbook = new Excel.Workbook();
|
|
217
|
+
|
|
218
|
+
workbook.SheetNames.forEach(sheetName => {
|
|
219
|
+
const worksheet = workbook.Sheets[sheetName];
|
|
220
|
+
const data = xlsx.utils.sheet_to_json(worksheet, { header: 1, defval: '' });
|
|
221
|
+
const newWorksheet = newWorkbook.addWorksheet(sheetName);
|
|
222
|
+
data.forEach(row => newWorksheet.addRow(row));
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const tempDir = path.join(path.dirname(filePath), '.tpp-excel-temp');
|
|
226
|
+
if (!fs.existsSync(tempDir)) {
|
|
227
|
+
fs.mkdirSync(tempDir, { recursive: true });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const fixedFilePath = path.join(
|
|
231
|
+
tempDir,
|
|
232
|
+
`fixed_${Date.now()}_${path.basename(filePath)}`
|
|
233
|
+
);
|
|
234
|
+
await newWorkbook.xlsx.writeFile(fixedFilePath);
|
|
235
|
+
|
|
236
|
+
logger.log(logLabel, `Fixed Excel file created at: ${fixedFilePath}`);
|
|
237
|
+
return fixedFilePath;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
const msg = error?.message || String(error);
|
|
240
|
+
logger.log(logLabel, `Failed to fix Excel file: ${msg}`);
|
|
241
|
+
throw { message: `Failed to fix Excel file: ${msg}` };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async readSheetAsArray(
|
|
246
|
+
filePath,
|
|
247
|
+
sheetName,
|
|
248
|
+
logLabel,
|
|
249
|
+
rowCount,
|
|
250
|
+
ignoreFirstRow,
|
|
251
|
+
retryCount,
|
|
252
|
+
fixedFilePath
|
|
253
|
+
) {
|
|
254
|
+
const logger = this._getLogger(logLabel);
|
|
255
|
+
filePath = this.fileUtils.getFilePath(filePath);
|
|
256
|
+
|
|
257
|
+
if (!fs.existsSync(filePath)) {
|
|
258
|
+
logger.failureLog(logLabel);
|
|
259
|
+
throw { message: `Excel file ${filePath} does not exists!` };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const workbookReader = new Excel.stream.xlsx.WorkbookReader(filePath);
|
|
264
|
+
let sheetFound = false;
|
|
265
|
+
const result = [];
|
|
266
|
+
|
|
267
|
+
for await (const sheet of workbookReader) {
|
|
268
|
+
if (sheetName && sheet.name !== sheetName) continue;
|
|
269
|
+
|
|
270
|
+
sheetFound = true;
|
|
271
|
+
|
|
272
|
+
for await (const row of sheet) {
|
|
273
|
+
if (!row || !row.values) continue;
|
|
274
|
+
const cleanedRow = row.values.slice(1);
|
|
275
|
+
result.push(cleanedRow);
|
|
276
|
+
|
|
277
|
+
if (rowCount && row.number > rowCount) {
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!sheetFound) {
|
|
285
|
+
const message = sheetName
|
|
286
|
+
? `Sheet with name ${sheetName} not found!`
|
|
287
|
+
: 'No sheet found in the file!';
|
|
288
|
+
logger.failureLog(logLabel);
|
|
289
|
+
throw { message };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Sheet found but nothing read -> try auto-fix
|
|
293
|
+
if (result.length === 0 && !retryCount && !fixedFilePath) {
|
|
294
|
+
logger.log(logLabel, 'Sheet found but no rows were read. Attempting to fix file...');
|
|
295
|
+
try {
|
|
296
|
+
const newFixedPath = await this._fixFileWithXlsxAndSleep(filePath, logLabel, logger);
|
|
297
|
+
const fixedResult = await this.readFixedFileAsArray(
|
|
298
|
+
newFixedPath,
|
|
299
|
+
sheetName,
|
|
300
|
+
logLabel,
|
|
301
|
+
rowCount,
|
|
302
|
+
ignoreFirstRow
|
|
303
|
+
);
|
|
304
|
+
this._cleanupTempFile(newFixedPath);
|
|
305
|
+
return fixedResult;
|
|
306
|
+
} catch (fixError) {
|
|
307
|
+
logger.log(logLabel, `File fix attempt failed: ${fixError.message}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (ignoreFirstRow && result.length > 0) {
|
|
312
|
+
return result.slice(1);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return result;
|
|
316
|
+
} catch (err) {
|
|
317
|
+
const message = err?.message || '';
|
|
318
|
+
|
|
319
|
+
// Special invalid signature case
|
|
320
|
+
if (!retryCount && message.includes('invalid signature: 0x4a4')) {
|
|
321
|
+
const newPath = await this.extractAndRewriteCleanXlsx(filePath, logLabel);
|
|
322
|
+
await sleep(1000);
|
|
323
|
+
return await this.readSheetAsArray(
|
|
324
|
+
newPath,
|
|
325
|
+
sheetName,
|
|
326
|
+
logLabel,
|
|
327
|
+
rowCount,
|
|
328
|
+
ignoreFirstRow,
|
|
329
|
+
1,
|
|
330
|
+
fixedFilePath
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Stream "reading 'sheets'" error handling
|
|
335
|
+
if (this._isSheetsReadError(err)) {
|
|
336
|
+
// try auto-fix first
|
|
337
|
+
if (!retryCount && !fixedFilePath) {
|
|
338
|
+
logger.log(logLabel, 'Error reading sheets. Attempting to fix file...');
|
|
339
|
+
try {
|
|
340
|
+
const newFixedPath = await this._fixFileWithXlsxAndSleep(filePath, logLabel, logger);
|
|
341
|
+
const fixedResult = await this.readFixedFileAsArray(
|
|
342
|
+
newFixedPath,
|
|
343
|
+
sheetName,
|
|
344
|
+
logLabel,
|
|
345
|
+
rowCount,
|
|
346
|
+
ignoreFirstRow
|
|
347
|
+
);
|
|
348
|
+
this._cleanupTempFile(newFixedPath);
|
|
349
|
+
return fixedResult;
|
|
350
|
+
} catch (fixError) {
|
|
351
|
+
if (fixError.message?.includes('not found')) {
|
|
352
|
+
throw fixError;
|
|
353
|
+
}
|
|
354
|
+
logger.log(
|
|
355
|
+
logLabel,
|
|
356
|
+
`File fix attempt failed: ${fixError.message}. Retrying with original file...`
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Retry with original file
|
|
362
|
+
if (!fixedFilePath && (!retryCount || retryCount < 4)) {
|
|
363
|
+
console.log('Error in iterating sheets. Retrying...', message);
|
|
364
|
+
await sleep(5000);
|
|
365
|
+
return await this.readSheetAsArray(
|
|
366
|
+
filePath,
|
|
367
|
+
sheetName,
|
|
368
|
+
logLabel,
|
|
369
|
+
rowCount,
|
|
370
|
+
ignoreFirstRow,
|
|
371
|
+
(retryCount || 0) + 1,
|
|
372
|
+
fixedFilePath
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
throw err;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
throw err;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// ---------- Compare files ----------
|
|
384
|
+
|
|
385
|
+
async compareExcelFiles(
|
|
386
|
+
filePath1,
|
|
387
|
+
filePath2,
|
|
388
|
+
sheetName1,
|
|
389
|
+
sheetName2,
|
|
390
|
+
allSheets,
|
|
391
|
+
logLabel,
|
|
392
|
+
retryCount,
|
|
393
|
+
fixedFilePath1,
|
|
394
|
+
fixedFilePath2
|
|
395
|
+
) {
|
|
396
|
+
const logger = this._getLogger(logLabel);
|
|
397
|
+
|
|
398
|
+
filePath1 = this._ensureFileExists(filePath1, logger, logLabel);
|
|
399
|
+
filePath2 = this._ensureFileExists(filePath2, logger, logLabel);
|
|
400
|
+
|
|
401
|
+
if (filePath1.toLowerCase() === filePath2.toLowerCase()) {
|
|
402
|
+
logger.failureLog(logLabel);
|
|
403
|
+
throw { message: 'Given file paths are same!' };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Check file sizes - if both are < 2MB, use xlsx library for comparison
|
|
407
|
+
const fileSize1 = fs.statSync(filePath1).size;
|
|
408
|
+
const fileSize2 = fs.statSync(filePath2).size;
|
|
409
|
+
const twoMB = 2 * 1024 * 1024; // 2MB in bytes
|
|
410
|
+
|
|
411
|
+
if (fileSize1 < twoMB && fileSize2 < twoMB) {
|
|
412
|
+
const workbook1 = xlsx.readFile(filePath1);
|
|
413
|
+
const workbook2 = xlsx.readFile(filePath2);
|
|
414
|
+
|
|
415
|
+
const failures = [];
|
|
416
|
+
let sheetFound = false;
|
|
417
|
+
|
|
418
|
+
for (const sheetName of workbook1.SheetNames) {
|
|
419
|
+
if (sheetName1 && sheetName !== sheetName1) {
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
sheetFound = true;
|
|
424
|
+
|
|
425
|
+
const targetSheetName = sheetName2 || sheetName;
|
|
426
|
+
if (!workbook2.SheetNames.includes(targetSheetName)) {
|
|
427
|
+
logger.failureLog(logLabel);
|
|
428
|
+
throw { message: `Sheet ${sheetName} not found in file2` };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const worksheet1 = workbook1.Sheets[sheetName];
|
|
432
|
+
const worksheet2 = workbook2.Sheets[targetSheetName];
|
|
433
|
+
const data1 = xlsx.utils.sheet_to_json(worksheet1, { header: 1, defval: '' });
|
|
434
|
+
const data2 = xlsx.utils.sheet_to_json(worksheet2, { header: 1, defval: '' });
|
|
435
|
+
|
|
436
|
+
const maxRows = Math.max(data1.length, data2.length);
|
|
437
|
+
for (let i = 0; i < maxRows; i++) {
|
|
438
|
+
const row1 = data1[i] || [];
|
|
439
|
+
const row2 = data2[i] || [];
|
|
440
|
+
|
|
441
|
+
this._compareAndLogRowDifference(row1, row2, i + 1, sheetName, allSheets, logLabel, logger, failures);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (!sheetName1 && !allSheets) {
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (!sheetFound) {
|
|
450
|
+
const message = sheetName1
|
|
451
|
+
? `Sheet with name ${sheetName1} not found!`
|
|
452
|
+
: 'No sheet found in the first file!';
|
|
453
|
+
logger.failureLog(logLabel);
|
|
454
|
+
throw { message };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (failures.length) {
|
|
458
|
+
logger.failureLog(logLabel);
|
|
459
|
+
throw { message: `There are ${failures.length} rows that do not match in the 2 files!` };
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
try {
|
|
466
|
+
const workbookReader1 = new Excel.stream.xlsx.WorkbookReader(filePath1);
|
|
467
|
+
let sheetFound = false;
|
|
468
|
+
const failures = [];
|
|
469
|
+
let rowsRead = 0;
|
|
470
|
+
|
|
471
|
+
for await (const sheet1 of workbookReader1) {
|
|
472
|
+
console.log(`Reading sheet1: ${sheet1.name}`);
|
|
473
|
+
|
|
474
|
+
if (sheetName1 && sheet1.name !== sheetName1) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
sheetFound = true;
|
|
479
|
+
|
|
480
|
+
const sheet2 = await this.getMatchingSheet(
|
|
481
|
+
filePath2,
|
|
482
|
+
sheetName2 || sheet1.name,
|
|
483
|
+
undefined,
|
|
484
|
+
fixedFilePath2
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
if (!sheet2) {
|
|
488
|
+
logger.failureLog(logLabel);
|
|
489
|
+
throw { message: `Sheet ${sheet1.name} not found in file2` };
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const iter1 = sheet1[Symbol.asyncIterator]();
|
|
493
|
+
const iter2 = sheet2[Symbol.asyncIterator]();
|
|
494
|
+
|
|
495
|
+
while (true) {
|
|
496
|
+
const [res1, res2] = await Promise.all([iter1.next(), iter2.next()]);
|
|
497
|
+
|
|
498
|
+
if (res1.done && res2.done) break;
|
|
499
|
+
|
|
500
|
+
const row1 = res1.value?.values?.slice(1) || [];
|
|
501
|
+
const row2 = res2.value?.values?.slice(1) || [];
|
|
502
|
+
|
|
503
|
+
if (res1.value || res2.value) {
|
|
504
|
+
rowsRead++;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const rowNumber = res1.value?.number || res2.value?.number || '';
|
|
508
|
+
this._compareAndLogRowDifference(row1, row2, rowNumber, sheet1.name, allSheets, logLabel, logger, failures);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (!sheetName1 && !allSheets) {
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// If sheet found but nothing read -> try fix
|
|
517
|
+
if (sheetFound && rowsRead === 0 && !retryCount && !fixedFilePath1 && !fixedFilePath2) {
|
|
518
|
+
logger.log(logLabel, 'Sheets found but no rows were read. Attempting to fix files...');
|
|
519
|
+
try {
|
|
520
|
+
const newFixedPath1 = await this._fixFileWithXlsxAndSleep(filePath1, logLabel, logger);
|
|
521
|
+
const newFixedPath2 = await this._fixFileWithXlsxAndSleep(filePath2, logLabel, logger);
|
|
522
|
+
const result = await this.compareExcelFiles(
|
|
523
|
+
newFixedPath1,
|
|
524
|
+
newFixedPath2,
|
|
525
|
+
sheetName1,
|
|
526
|
+
sheetName2,
|
|
527
|
+
allSheets,
|
|
528
|
+
logLabel,
|
|
529
|
+
1,
|
|
530
|
+
newFixedPath1,
|
|
531
|
+
newFixedPath2
|
|
532
|
+
);
|
|
533
|
+
this._cleanupTempFile(newFixedPath1);
|
|
534
|
+
this._cleanupTempFile(newFixedPath2);
|
|
535
|
+
return result;
|
|
536
|
+
} catch (fixError) {
|
|
537
|
+
logger.log(logLabel, `File fix attempt failed: ${fixError.message}`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (!sheetFound) {
|
|
542
|
+
const message = sheetName1
|
|
543
|
+
? `Sheet with name ${sheetName1} not found!`
|
|
544
|
+
: 'No sheet found in the first file!';
|
|
545
|
+
logger.failureLog(logLabel);
|
|
546
|
+
throw { message };
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (failures.length) {
|
|
550
|
+
logger.failureLog(logLabel);
|
|
551
|
+
throw { message: `There are ${failures.length} rows that do not match in the 2 files!` };
|
|
552
|
+
}
|
|
553
|
+
} catch (err) {
|
|
554
|
+
const message = err?.message || '';
|
|
555
|
+
|
|
556
|
+
if (!retryCount && message.includes('invalid signature: 0x4a4')) {
|
|
557
|
+
const newPath1 = await this.extractAndRewriteCleanXlsx(filePath1, logLabel);
|
|
558
|
+
await sleep(1000);
|
|
559
|
+
const newPath2 = await this.extractAndRewriteCleanXlsx(filePath2, logLabel);
|
|
560
|
+
await sleep(1000);
|
|
561
|
+
return await this.compareExcelFiles(
|
|
562
|
+
newPath1,
|
|
563
|
+
newPath2,
|
|
564
|
+
sheetName1,
|
|
565
|
+
sheetName2,
|
|
566
|
+
allSheets,
|
|
567
|
+
logLabel,
|
|
568
|
+
1,
|
|
569
|
+
fixedFilePath1,
|
|
570
|
+
fixedFilePath2
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (this._isSheetsReadError(err)) {
|
|
575
|
+
if (!retryCount && !fixedFilePath1 && !fixedFilePath2) {
|
|
576
|
+
logger.log(logLabel, 'Error reading sheets. Attempting to fix files...');
|
|
577
|
+
try {
|
|
578
|
+
const newFixedPath1 = await this._fixFileWithXlsxAndSleep(filePath1, logLabel, logger);
|
|
579
|
+
const newFixedPath2 = await this._fixFileWithXlsxAndSleep(filePath2, logLabel, logger);
|
|
580
|
+
const result = await this.compareExcelFiles(
|
|
581
|
+
newFixedPath1,
|
|
582
|
+
newFixedPath2,
|
|
583
|
+
sheetName1,
|
|
584
|
+
sheetName2,
|
|
585
|
+
allSheets,
|
|
586
|
+
logLabel,
|
|
587
|
+
1,
|
|
588
|
+
newFixedPath1,
|
|
589
|
+
newFixedPath2
|
|
590
|
+
);
|
|
591
|
+
this._cleanupTempFile(newFixedPath1);
|
|
592
|
+
this._cleanupTempFile(newFixedPath2);
|
|
593
|
+
return result;
|
|
594
|
+
} catch (fixError) {
|
|
595
|
+
logger.log(
|
|
596
|
+
logLabel,
|
|
597
|
+
`File fix attempt failed: ${fixError.message}. Retrying with original files...`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (!retryCount || retryCount < 4) {
|
|
603
|
+
console.log('Error in iterating sheets. Retrying...', message);
|
|
604
|
+
await sleep(5000);
|
|
605
|
+
return await this.compareExcelFiles(
|
|
606
|
+
filePath1,
|
|
607
|
+
filePath2,
|
|
608
|
+
sheetName1,
|
|
609
|
+
sheetName2,
|
|
610
|
+
allSheets,
|
|
611
|
+
logLabel,
|
|
612
|
+
(retryCount || 0) + 1,
|
|
613
|
+
fixedFilePath1,
|
|
614
|
+
fixedFilePath2
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
throw err;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
throw err;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
async getMatchingSheet(filePath, targetSheetName, retry, fixedFilePath) {
|
|
626
|
+
try {
|
|
627
|
+
const workbookReader = new Excel.stream.xlsx.WorkbookReader(filePath);
|
|
628
|
+
let sheetFound = false;
|
|
629
|
+
|
|
630
|
+
for await (const sheet of workbookReader) {
|
|
631
|
+
if (sheet.name === targetSheetName) {
|
|
632
|
+
sheetFound = true;
|
|
633
|
+
let columnCount = sheet.dimensions?.model?.right || 0;
|
|
634
|
+
|
|
635
|
+
if (columnCount === 0 && !retry && !fixedFilePath) {
|
|
636
|
+
try {
|
|
637
|
+
const newFixedPath = await this._fixFileWithXlsxAndSleep(
|
|
638
|
+
filePath,
|
|
639
|
+
`Finding sheet ${targetSheetName}`,
|
|
640
|
+
this._getLogger(`Finding sheet ${targetSheetName}`)
|
|
641
|
+
);
|
|
642
|
+
return await this.getMatchingSheet(newFixedPath, targetSheetName, 1, newFixedPath);
|
|
643
|
+
} catch {
|
|
644
|
+
// fallback to original
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return sheet;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (!sheetFound && !retry && !fixedFilePath) {
|
|
653
|
+
try {
|
|
654
|
+
const newFixedPath = await this._fixFileWithXlsxAndSleep(
|
|
655
|
+
filePath,
|
|
656
|
+
`Finding sheet ${targetSheetName}`,
|
|
657
|
+
this._getLogger(`Finding sheet ${targetSheetName}`)
|
|
658
|
+
);
|
|
659
|
+
return await this.getMatchingSheet(newFixedPath, targetSheetName, 1, newFixedPath);
|
|
660
|
+
} catch {
|
|
661
|
+
// return null below
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
} catch (er) {
|
|
665
|
+
console.log('Error reading matching sheet!', er.message);
|
|
666
|
+
if ((!retry || retry < 2) && this._isSheetsReadError(er)) {
|
|
667
|
+
console.log('Retrying find matching sheet on iterator error...', er.message);
|
|
668
|
+
await sleep(5000);
|
|
669
|
+
return await this.getMatchingSheet(filePath, targetSheetName, (retry || 0) + 1, fixedFilePath);
|
|
670
|
+
} else if (!retry && !fixedFilePath && !this._isSheetsReadError(er)) {
|
|
671
|
+
try {
|
|
672
|
+
const newFixedPath = await this._fixFileWithXlsxAndSleep(
|
|
673
|
+
filePath,
|
|
674
|
+
`Finding sheet ${targetSheetName}`,
|
|
675
|
+
this._getLogger(`Finding sheet ${targetSheetName}`)
|
|
676
|
+
);
|
|
677
|
+
return await this.getMatchingSheet(newFixedPath, targetSheetName, 1, newFixedPath);
|
|
678
|
+
} catch (fixError) {
|
|
679
|
+
console.log(`Not able to find sheet ${targetSheetName}`, er);
|
|
680
|
+
throw er;
|
|
681
|
+
}
|
|
682
|
+
} else {
|
|
683
|
+
console.log(`Not able to find sheet ${targetSheetName}`, er);
|
|
684
|
+
throw er;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// ---------- Advanced read (modularized) ----------
|
|
692
|
+
|
|
693
|
+
async advanced_excel_read(params) {
|
|
694
|
+
const logLabel = 'Excel.Advanced Read';
|
|
695
|
+
const logger = this._getLogger(logLabel);
|
|
696
|
+
logger.startLog(logLabel);
|
|
697
|
+
|
|
698
|
+
if (!params.filePath?.trim()) {
|
|
699
|
+
logger.failureLog(logLabel);
|
|
700
|
+
throw { message: 'No file path provided!' };
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
const filePath = this._ensureFileExists(params.filePath, logger, logLabel);
|
|
704
|
+
const sheetName = params.sheetName?.trim() || undefined;
|
|
705
|
+
const ignore = getBoolean(params.ignoreMissingData);
|
|
706
|
+
const isFirstRowHeader = getBoolean(params.isFirstRowHeader);
|
|
707
|
+
const getRowsAsObjects = getBoolean(params.getRowsAsObjects);
|
|
708
|
+
|
|
709
|
+
// Read header row once
|
|
710
|
+
const sheetData = await this.readSheetAsArray(filePath, sheetName, logLabel, 1);
|
|
711
|
+
const header = sheetData?.at(0) || [];
|
|
712
|
+
const firstRowHeader = isFirstRowHeader && sheetData?.length > 0;
|
|
713
|
+
|
|
714
|
+
let fields = firstRowHeader
|
|
715
|
+
? header.map(field => field.trim())
|
|
716
|
+
: header.map((_h, index) => index);
|
|
717
|
+
|
|
718
|
+
const allFields = fields.slice();
|
|
719
|
+
|
|
720
|
+
// Filter rows
|
|
721
|
+
const rowsFilter = params.rows || 'all';
|
|
722
|
+
const dataRows = await this._readRowsWithFilter({
|
|
723
|
+
filePath,
|
|
724
|
+
sheetName,
|
|
725
|
+
logLabel,
|
|
726
|
+
rowsFilter,
|
|
727
|
+
ignore,
|
|
728
|
+
isFirstRowHeader
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
// Filter columns
|
|
732
|
+
fields = new TableUtils(this.caller, this.blacklistedLogs, logLabel).filterColumns(
|
|
733
|
+
params,
|
|
734
|
+
fields,
|
|
735
|
+
ignore,
|
|
736
|
+
firstRowHeader
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
const dataObject = this._buildDataObject({
|
|
740
|
+
dataRows,
|
|
741
|
+
fields,
|
|
742
|
+
allFields,
|
|
743
|
+
getRowsAsObjects,
|
|
744
|
+
firstRowHeader
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
logger.endLog(logLabel);
|
|
748
|
+
return dataObject;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
async _readRowsWithFilter({
|
|
752
|
+
filePath,
|
|
753
|
+
sheetName,
|
|
754
|
+
logLabel,
|
|
755
|
+
rowsFilter,
|
|
756
|
+
ignore,
|
|
757
|
+
isFirstRowHeader
|
|
758
|
+
}) {
|
|
759
|
+
const filter = rowsFilter?.toString().trim().toLowerCase() || 'all';
|
|
760
|
+
|
|
761
|
+
const readAll = () =>
|
|
762
|
+
this.readSheetAsArray(filePath, sheetName, logLabel, undefined, isFirstRowHeader);
|
|
763
|
+
|
|
764
|
+
if (filter === 'all') {
|
|
765
|
+
return await readAll();
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (filter.startsWith('last')) {
|
|
769
|
+
const countStr = filter.replace('last', '').trim();
|
|
770
|
+
if (!isNumber(countStr)) {
|
|
771
|
+
throw { message: `${rowsFilter} is not a valid rows filter!` };
|
|
772
|
+
}
|
|
773
|
+
const count = parseInt(countStr, 10);
|
|
774
|
+
let dataRows = await this.readSheetAsArray(
|
|
775
|
+
filePath,
|
|
776
|
+
sheetName,
|
|
777
|
+
logLabel,
|
|
778
|
+
undefined,
|
|
779
|
+
isFirstRowHeader
|
|
780
|
+
);
|
|
781
|
+
const data = getLastElements(dataRows, count, ignore);
|
|
782
|
+
if (!data.success) throw { message: data.error };
|
|
783
|
+
return data.data;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (filter.startsWith('first')) {
|
|
787
|
+
const countStr = filter.replace('first', '').trim();
|
|
788
|
+
if (!isNumber(countStr)) {
|
|
789
|
+
throw { message: `${rowsFilter} is not a valid rows filter!` };
|
|
790
|
+
}
|
|
791
|
+
const count = parseInt(countStr, 10);
|
|
792
|
+
let dataRows = await this.readSheetAsArray(
|
|
793
|
+
filePath,
|
|
794
|
+
sheetName,
|
|
795
|
+
logLabel,
|
|
796
|
+
count,
|
|
797
|
+
isFirstRowHeader
|
|
798
|
+
);
|
|
799
|
+
const data = getFirstElements(dataRows, count, ignore);
|
|
800
|
+
if (!data.success) throw { message: data.error };
|
|
801
|
+
return data.data;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (filter.startsWith('any')) {
|
|
805
|
+
const countStr = filter.replace('any', '').trim();
|
|
806
|
+
if (!isNumber(countStr)) {
|
|
807
|
+
throw { message: `${rowsFilter} is not a valid rows filter!` };
|
|
808
|
+
}
|
|
809
|
+
const count = parseInt(countStr, 10);
|
|
810
|
+
let dataRows = await this.readSheetAsArray(
|
|
811
|
+
filePath,
|
|
812
|
+
sheetName,
|
|
813
|
+
logLabel,
|
|
814
|
+
count + 10, // now correctly numeric
|
|
815
|
+
isFirstRowHeader
|
|
816
|
+
);
|
|
817
|
+
const data = getAnyElements(dataRows, count, ignore);
|
|
818
|
+
if (!data.success) throw { message: data.error };
|
|
819
|
+
return data.data;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
if (filter.includes('-')) {
|
|
823
|
+
const [, y] = filter.split('-').map(Number);
|
|
824
|
+
let dataRows = await this.readSheetAsArray(
|
|
825
|
+
filePath,
|
|
826
|
+
sheetName,
|
|
827
|
+
logLabel,
|
|
828
|
+
y + 10,
|
|
829
|
+
isFirstRowHeader
|
|
830
|
+
);
|
|
831
|
+
const data = getElementsInRange(dataRows, filter, ignore);
|
|
832
|
+
if (!data.success) throw { message: data.error };
|
|
833
|
+
return data.data;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (filter.includes(',')) {
|
|
837
|
+
const indexArray = filter.split(',').map(Number);
|
|
838
|
+
const max = Math.max(...indexArray);
|
|
839
|
+
let dataRows = await this.readSheetAsArray(
|
|
840
|
+
filePath,
|
|
841
|
+
sheetName,
|
|
842
|
+
logLabel,
|
|
843
|
+
max + 10,
|
|
844
|
+
isFirstRowHeader
|
|
845
|
+
);
|
|
846
|
+
const data = getElementsAtIndices(dataRows, filter, ignore);
|
|
847
|
+
if (!data.success) throw { message: data.error };
|
|
848
|
+
return data.data;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// single index
|
|
852
|
+
if (!isNumber(filter)) {
|
|
853
|
+
throw { message: `${rowsFilter} is not a valid rows filter!` };
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
const index = parseInt(filter.trim(), 10);
|
|
857
|
+
let dataRows = await this.readSheetAsArray(
|
|
858
|
+
filePath,
|
|
859
|
+
sheetName,
|
|
860
|
+
logLabel,
|
|
861
|
+
index + 2,
|
|
862
|
+
isFirstRowHeader
|
|
863
|
+
);
|
|
864
|
+
|
|
865
|
+
if (index < 0 || (index >= dataRows.length && !ignore)) {
|
|
866
|
+
throw { message: `${rowsFilter} - index is invalid. Data size - ${dataRows.length}` };
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (ignore && index >= dataRows.length) {
|
|
870
|
+
return [];
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
return [dataRows[index]];
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
_buildDataObject({ dataRows, fields, allFields, getRowsAsObjects, firstRowHeader }) {
|
|
877
|
+
return dataRows.map(cells => {
|
|
878
|
+
const row = getRowsAsObjects ? {} : [];
|
|
879
|
+
for (let i = 0; i < allFields.length; i++) {
|
|
880
|
+
const skip = firstRowHeader ? !fields.includes(allFields[i]) : !fields.includes(i);
|
|
881
|
+
if (skip) continue;
|
|
882
|
+
const val = i < cells.length ? cells[i] || '' : '';
|
|
883
|
+
if (getRowsAsObjects) {
|
|
884
|
+
const fieldName = firstRowHeader ? allFields[i] : `column_${i}`;
|
|
885
|
+
row[fieldName] = val;
|
|
886
|
+
} else {
|
|
887
|
+
row.push(val);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return row;
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// ---------- Repair via external tool ----------
|
|
895
|
+
|
|
896
|
+
async extractAndRewriteCleanXlsx(filePath, parentLogLabel) {
|
|
897
|
+
const logLabel = `${parentLogLabel} :: Excel file ${filePath} is not properly formatted, fixing the file!`;
|
|
898
|
+
const logger = this._getLogger(logLabel);
|
|
899
|
+
logger.startLog(logLabel);
|
|
900
|
+
|
|
901
|
+
const inputPath = this.fileUtils.getFilePath(filePath);
|
|
902
|
+
|
|
903
|
+
if (!this.dependenciesPath) {
|
|
904
|
+
logger.failureLog(logLabel);
|
|
905
|
+
throw {
|
|
906
|
+
message: 'Dependencies path is not defined! Please set it in the config file.'
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
const result = await processExcel(this.dependenciesPath, inputPath);
|
|
911
|
+
|
|
912
|
+
if (result.status !== 0) {
|
|
913
|
+
logger.failureLog(logLabel);
|
|
914
|
+
throw { message: result.message };
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
logger.endLog(logLabel);
|
|
918
|
+
return result.message;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
exports.ExcelUtils = ExcelUtils;
|