@questwork/q-utilities 0.1.34 → 0.1.35

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.
@@ -1954,10 +1954,280 @@ function downloadFileByUrl({ dataUrl = "", filename = "" } = {}) {
1954
1954
  function escapeRegex(string) {
1955
1955
  return String(string).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
1956
1956
  }
1957
+ function init(_class, options) {
1958
+ if (options instanceof _class) {
1959
+ return options;
1960
+ }
1961
+ try {
1962
+ const instance2 = new _class(options);
1963
+ return instance2.isValid !== false ? instance2 : null;
1964
+ } catch (e) {
1965
+ console.log(`init failed for class: ${_class._classname || "no _classname"}`, e);
1966
+ return null;
1967
+ }
1968
+ }
1969
+ function initFromArray(_class, arr) {
1970
+ if (Array.isArray(arr)) {
1971
+ return arr.map((a) => init(_class, a));
1972
+ }
1973
+ return [];
1974
+ }
1975
+ function initOnlyValidFromArray(_class, arr) {
1976
+ return initFromArray(_class, arr).filter((i) => i);
1977
+ }
1978
+ const DATAWRAPPER_STATUS = {
1979
+ ORPHAN_DELETED: "orphanDeleted",
1980
+ EXISTED: "existed",
1981
+ FAILED: "failed",
1982
+ INSERTED: "inserted",
1983
+ SKIPPED: "skipped",
1984
+ UPDATED: "updated"
1985
+ };
1986
+ const LOG_TYPE = {
1987
+ IGNORE: "IGNORE",
1988
+ DUPLICATED: "DUPLICATED",
1989
+ REQUIRED_ERROR: "REQUIRED_ERROR",
1990
+ TRANSFORM_ERROR: "TRANSFORM_ERROR",
1991
+ CONVERT_ERROR: "CONVERT_ERROR",
1992
+ ORPHAN_DELETE_ERROR: "ORPHAN_DELETE_ERROR",
1993
+ INSERT_ERROR: "INSERT_ERROR",
1994
+ UPDATE_ERROR: "UPDATE_ERROR",
1995
+ SKIPPED: "SKIPPED",
1996
+ INSERTED: "INSERTED",
1997
+ UPDATED: "UPDATED",
1998
+ ORPHAN_DELETED: "ORPHAN_DELETED"
1999
+ };
2000
+ class DataWrapper {
2001
+ constructor(opts) {
2002
+ this.data = opts.data || {};
2003
+ this.logs = opts.logs || [];
2004
+ this.rawData = opts.rawData || {};
2005
+ this.rowIndex = opts.rowIndex || 0;
2006
+ this.status = opts.status || {
2007
+ orphanDeleted: null,
2008
+ existed: null,
2009
+ failed: null,
2010
+ inserted: null,
2011
+ updated: null
2012
+ };
2013
+ this.transformedData = opts.transformedData || {};
2014
+ }
2015
+ static init(opts) {
2016
+ return init(this, opts);
2017
+ }
2018
+ static initFromArray(arr = []) {
2019
+ return initFromArray(this, arr);
2020
+ }
2021
+ static initOnlyValidFromArray(arr = []) {
2022
+ return initOnlyValidFromArray(this, arr);
2023
+ }
2024
+ get errorLogs() {
2025
+ const excludedTypes = [LOG_TYPE.IGNORE, LOG_TYPE.SKIPPED, LOG_TYPE.DUPLICATED];
2026
+ return this.logs.filter((l) => !excludedTypes.includes(l.type));
2027
+ }
2028
+ get ignoreLog() {
2029
+ return this.logs.find((l) => l.type === LOG_TYPE.IGNORE);
2030
+ }
2031
+ addLog(detail) {
2032
+ this.logs.push({
2033
+ ...detail,
2034
+ rowIndex: this.rowIndex
2035
+ });
2036
+ return this;
2037
+ }
2038
+ addError(detail) {
2039
+ return this.addLog(detail).setFailed();
2040
+ }
2041
+ addOrphanDeleteError(msg) {
2042
+ this.addError({
2043
+ column: "",
2044
+ message: msg || "Delete failed.",
2045
+ type: LOG_TYPE.ORPHAN_DELETE_ERROR
2046
+ });
2047
+ return this;
2048
+ }
2049
+ addInsertError(msg) {
2050
+ this.addError({
2051
+ column: "",
2052
+ message: msg || "Insert failed.",
2053
+ type: LOG_TYPE.INSERT_ERROR
2054
+ });
2055
+ return this;
2056
+ }
2057
+ addUpdateError(msg) {
2058
+ this.addError({
2059
+ column: "",
2060
+ message: msg || "Update failed.",
2061
+ type: LOG_TYPE.UPDATE_ERROR
2062
+ });
2063
+ return this;
2064
+ }
2065
+ addDuplicatedLog(msg) {
2066
+ return this.addLog({
2067
+ column: "",
2068
+ message: msg || "Duplicated.",
2069
+ type: LOG_TYPE.DUPLICATED
2070
+ });
2071
+ }
2072
+ addSkippedLog(msg) {
2073
+ return this.addLog({
2074
+ column: "",
2075
+ message: msg || "Skipped.",
2076
+ type: LOG_TYPE.SKIPPED
2077
+ }).setSkipped();
2078
+ }
2079
+ addIgnoreLog(msg) {
2080
+ this.logs = this.logs.filter((l) => l.type !== LOG_TYPE.IGNORE);
2081
+ return this.addLog({
2082
+ column: "",
2083
+ message: msg || "Ignored.",
2084
+ type: LOG_TYPE.IGNORE
2085
+ });
2086
+ }
2087
+ addInsertedLog(msg) {
2088
+ return this.addLog({
2089
+ column: "",
2090
+ message: msg || "Inserted.",
2091
+ type: LOG_TYPE.INSERTED
2092
+ }).setInserted();
2093
+ }
2094
+ addUpdatedLog(msg) {
2095
+ return this.addLog({
2096
+ column: "",
2097
+ message: msg || "Updated.",
2098
+ type: LOG_TYPE.UPDATED
2099
+ }).setUpdated();
2100
+ }
2101
+ addOrphanDeletedLog(msg) {
2102
+ return this.addLog({
2103
+ column: "",
2104
+ message: msg || "Orphan deleted.",
2105
+ type: LOG_TYPE.ORPHAN_DELETED
2106
+ }).setOrphanDeleted();
2107
+ }
2108
+ isCheckValid() {
2109
+ return this.errorLogs.length === 0 && !this.ignoreLog && !this.logs.some((l) => l.type === LOG_TYPE.DUPLICATED);
2110
+ }
2111
+ isIgnored() {
2112
+ return !!this.ignoreLog;
2113
+ }
2114
+ isDuplicated() {
2115
+ return this.logs.some((log) => log.type === LOG_TYPE.DUPLICATED);
2116
+ }
2117
+ hasErrors() {
2118
+ return this.errorLogs.length > 0;
2119
+ }
2120
+ setStatus(status) {
2121
+ this.status[DATAWRAPPER_STATUS[status]] = Date.now();
2122
+ return this;
2123
+ }
2124
+ setExisted() {
2125
+ this.setStatus("EXISTED");
2126
+ return this;
2127
+ }
2128
+ setFailed() {
2129
+ this.setStatus("FAILED");
2130
+ return this;
2131
+ }
2132
+ setInserted() {
2133
+ this.setStatus("INSERTED");
2134
+ return this;
2135
+ }
2136
+ setSkipped() {
2137
+ this.setStatus("SKIPPED");
2138
+ return this;
2139
+ }
2140
+ setUpdated() {
2141
+ this.setStatus("UPDATED");
2142
+ return this;
2143
+ }
2144
+ setOrphanDeleted() {
2145
+ this.setStatus("ORPHAN_DELETED");
2146
+ return this;
2147
+ }
2148
+ setData(data) {
2149
+ this.data = data;
2150
+ return this;
2151
+ }
2152
+ setRawData(rawData) {
2153
+ this.rawData = rawData;
2154
+ return this;
2155
+ }
2156
+ setTransformedData(transformedData) {
2157
+ this.transformedData = transformedData;
2158
+ return this;
2159
+ }
2160
+ }
2161
+ class DataSet {
2162
+ constructor(opts) {
2163
+ opts = opts || {};
2164
+ this.name = opts.name || "";
2165
+ this.data = DataWrapper.initOnlyValidFromArray(opts.data || []);
2166
+ this.orphanData = DataWrapper.initOnlyValidFromArray(opts.orphanData || []);
2167
+ this.checkResultConfig = opts.checkResultConfig ? { ...opts.checkResultConfig } : {};
2168
+ this.checkResultConfig.hidden = this.checkResultConfig.hidden ?? false;
2169
+ this.saveResultConfig = opts.saveResultConfig ? { ...opts.saveResultConfig } : {};
2170
+ this.saveResultConfig.hidden = this.saveResultConfig.hidden ?? false;
2171
+ this.orphanBehavior = opts.orphanBehavior ?? null;
2172
+ this.notExistBehavior = opts.notExistBehavior;
2173
+ this.existBehavior = opts.existBehavior;
2174
+ }
2175
+ static init(opts) {
2176
+ return init(this, opts);
2177
+ }
2178
+ static initFromArray(arr = []) {
2179
+ return initFromArray(this, arr);
2180
+ }
2181
+ static initOnlyValidFromArray(arr = []) {
2182
+ return initOnlyValidFromArray(this, arr);
2183
+ }
2184
+ add(data) {
2185
+ this.data.push(data);
2186
+ return this;
2187
+ }
2188
+ getExistedData() {
2189
+ return this.data.filter((item) => {
2190
+ var _a;
2191
+ return item.isCheckValid() && ((_a = item.status) == null ? void 0 : _a.existed);
2192
+ });
2193
+ }
2194
+ getInvalidData() {
2195
+ return this.data.filter((item) => !item.isCheckValid());
2196
+ }
2197
+ getNotExistedData() {
2198
+ return this.data.filter((item) => {
2199
+ var _a;
2200
+ return item.isCheckValid() && !((_a = item.status) == null ? void 0 : _a.existed);
2201
+ });
2202
+ }
2203
+ getSkipData() {
2204
+ return this.data.filter((item) => item.isIgnored() || item.isDuplicated());
2205
+ }
2206
+ addOrphan(data) {
2207
+ const dataWrapper = new DataWrapper({
2208
+ data,
2209
+ rowIndex: -1
2210
+ });
2211
+ this.orphanData.push(dataWrapper);
2212
+ return this;
2213
+ }
2214
+ getOrphanData() {
2215
+ return this.orphanData;
2216
+ }
2217
+ }
1957
2218
  class CheckResult {
1958
2219
  constructor(opts) {
1959
2220
  this.dataSets = [];
1960
- this.dataSets = (opts == null ? void 0 : opts.dataSets) || [];
2221
+ this.dataSets = DataSet.initOnlyValidFromArray((opts == null ? void 0 : opts.dataSets) || []);
2222
+ }
2223
+ static init(opts) {
2224
+ return init(this, opts);
2225
+ }
2226
+ static initFromArray(arr = []) {
2227
+ return initFromArray(this, arr);
2228
+ }
2229
+ static initOnlyValidFromArray(arr = []) {
2230
+ return initOnlyValidFromArray(this, arr);
1961
2231
  }
1962
2232
  addDataSet(dataSet) {
1963
2233
  this.dataSets.push(dataSet);
@@ -1965,16 +2235,21 @@ class CheckResult {
1965
2235
  getDataSetByName(name) {
1966
2236
  return this.dataSets.find((dataSet) => dataSet.name === name);
1967
2237
  }
2238
+ getAllSummary() {
2239
+ return {
2240
+ summary: this.getSummary()
2241
+ };
2242
+ }
1968
2243
  getSummary() {
1969
2244
  return this.dataSets.map((dataSet) => ({
1970
2245
  name: dataSet.name,
1971
2246
  hidden: dataSet.checkResultConfig.hidden,
1972
2247
  summary: {
1973
2248
  total: dataSet.data.length,
1974
- valid: dataSet.data.filter((row) => row.errorLogs.length === 0 && !row.ignoreLog).length,
1975
- ignored: dataSet.data.filter((row) => row.ignoreLog).length,
1976
- invalid: dataSet.data.filter((row) => row.errorLogs.length > 0 && !row.errorLogs.some((detail) => detail.type === "DUPLICATED")).length,
1977
- duplicated: dataSet.data.filter((row) => row.errorLogs.some((detail) => detail.type === "DUPLICATED")).length
2249
+ ignored: dataSet.data.filter((row) => row.isIgnored()).length,
2250
+ duplicated: dataSet.data.filter((row) => !row.isIgnored() && row.isDuplicated()).length,
2251
+ invalid: dataSet.data.filter((row) => !row.isIgnored() && !row.isDuplicated() && row.hasErrors()).length,
2252
+ valid: dataSet.data.filter((row) => row.isCheckValid()).length
1978
2253
  }
1979
2254
  }));
1980
2255
  }
@@ -1982,8 +2257,8 @@ class CheckResult {
1982
2257
  return this.dataSets.map((dataSet) => ({
1983
2258
  name: dataSet.name,
1984
2259
  hidden: dataSet.checkResultConfig.hidden,
1985
- logs: dataSet.data.filter((row) => row.errorLogs.some((detail) => detail.type === "DUPLICATED")).reduce((acc, row) => {
1986
- return [...acc, ...row.errorLogs];
2260
+ logs: dataSet.data.filter((row) => row.logs.some((detail) => detail.type === LOG_TYPE.DUPLICATED)).reduce((acc, row) => {
2261
+ return [...acc, ...row.logs.filter((detail) => detail.type === LOG_TYPE.DUPLICATED)];
1987
2262
  }, [])
1988
2263
  }));
1989
2264
  }
@@ -2000,21 +2275,33 @@ class CheckResult {
2000
2275
  return this.dataSets.map((dataSet) => ({
2001
2276
  name: dataSet.name,
2002
2277
  hidden: dataSet.checkResultConfig.hidden,
2003
- logs: dataSet.data.filter((row) => row.errorLogs.length > 0 && !row.errorLogs.some((detail) => detail.type === "DUPLICATED")).reduce((acc, row) => {
2278
+ logs: dataSet.data.filter((row) => row.errorLogs.length > 0).reduce((acc, row) => {
2004
2279
  return [...acc, ...row.errorLogs];
2005
2280
  }, [])
2006
2281
  }));
2007
2282
  }
2008
2283
  getValidDetails() {
2009
2284
  return this.dataSets.reduce((acc, dataSet) => {
2010
- acc[dataSet.name] = dataSet.data.filter((row) => row.errorLogs.length === 0).reduce((acc2, row) => {
2285
+ acc[dataSet.name] = dataSet.data.filter((row) => row.isCheckValid()).reduce((acc2, row) => {
2011
2286
  return [...acc2, row];
2012
2287
  }, []);
2013
2288
  return acc;
2014
2289
  }, {});
2015
2290
  }
2291
+ getAllDetails() {
2292
+ return {
2293
+ details: this.getDetails()
2294
+ };
2295
+ }
2296
+ getDetails() {
2297
+ return {
2298
+ ignoredDetails: this.getIgnoredDetails(),
2299
+ invalidDetails: this.getInvalidDetails(),
2300
+ duplicatedDetails: this.getDuplicatedDetails()
2301
+ };
2302
+ }
2016
2303
  hasInvalidData() {
2017
- return this.dataSets.some((dataSet) => dataSet.data.some((row) => row.errorLogs.length > 0 && !row.errorLogs.some((detail) => detail.type === "DUPLICATED")));
2304
+ return this.dataSets.some((dataSet) => dataSet.data.some((row) => row.errorLogs.length > 0));
2018
2305
  }
2019
2306
  }
2020
2307
  class SaveResult {
@@ -2027,11 +2314,112 @@ class SaveResult {
2027
2314
  this.dataSets.push(dataSet);
2028
2315
  return this;
2029
2316
  }
2317
+ getAllDetails() {
2318
+ return {
2319
+ details: this.getDetails(),
2320
+ orphanDetails: this.getOrphanDetails()
2321
+ };
2322
+ }
2323
+ getDetails() {
2324
+ return {
2325
+ failed: this.getFailedDetails(),
2326
+ skipped: this.getSkippedDetails(),
2327
+ inserted: this.getInsertedDetails(),
2328
+ updated: this.getUpdatedDetails()
2329
+ };
2330
+ }
2331
+ getFailedDetails() {
2332
+ const types = [LOG_TYPE.INSERT_ERROR, LOG_TYPE.UPDATE_ERROR];
2333
+ return this.dataSets.map((dataSet) => {
2334
+ return {
2335
+ name: dataSet.name,
2336
+ hidden: dataSet.saveResultConfig.hidden,
2337
+ logs: dataSet.data.filter((row) => row.logs.some((detail) => types.includes(detail.type))).reduce((acc, row) => {
2338
+ return acc.concat(row.logs.filter((detail) => types.includes(detail.type)));
2339
+ }, [])
2340
+ };
2341
+ });
2342
+ }
2343
+ getSkippedDetails() {
2344
+ const types = [LOG_TYPE.SKIPPED, LOG_TYPE.IGNORE, LOG_TYPE.DUPLICATED];
2345
+ return this.dataSets.map((dataSet) => {
2346
+ return {
2347
+ name: dataSet.name,
2348
+ hidden: dataSet.saveResultConfig.hidden,
2349
+ logs: dataSet.data.reduce((acc, row) => {
2350
+ const filtered = row.logs.filter((detail) => types.includes(detail.type));
2351
+ return acc.concat(filtered);
2352
+ }, [])
2353
+ };
2354
+ });
2355
+ }
2356
+ getInsertedDetails() {
2357
+ return this.dataSets.map((dataSet) => {
2358
+ return {
2359
+ name: dataSet.name,
2360
+ hidden: dataSet.saveResultConfig.hidden,
2361
+ logs: dataSet.data.reduce((acc, row) => {
2362
+ const filtered = row.logs.filter((detail) => detail.type === LOG_TYPE.INSERTED);
2363
+ return acc.concat(filtered);
2364
+ }, [])
2365
+ };
2366
+ });
2367
+ }
2368
+ getUpdatedDetails() {
2369
+ return this.dataSets.map((dataSet) => {
2370
+ return {
2371
+ name: dataSet.name,
2372
+ hidden: dataSet.saveResultConfig.hidden,
2373
+ logs: dataSet.data.reduce((acc, row) => {
2374
+ const filtered = row.logs.filter((detail) => detail.type === LOG_TYPE.UPDATED);
2375
+ return acc.concat(filtered);
2376
+ }, [])
2377
+ };
2378
+ });
2379
+ }
2380
+ getOrphanDetails() {
2381
+ return {
2382
+ deleted: this.getOrphanDeletedDetails(),
2383
+ failed: this.getOrphanFailedDetails()
2384
+ };
2385
+ }
2386
+ getOrphanFailedDetails() {
2387
+ return this.dataSets.reduce((acc, dataSet) => {
2388
+ if (dataSet.orphanBehavior) {
2389
+ acc.push({
2390
+ name: dataSet.name,
2391
+ hidden: dataSet.saveResultConfig.hidden,
2392
+ logs: dataSet.orphanData.filter((row) => {
2393
+ var _a;
2394
+ return (_a = row.errorLogs) == null ? void 0 : _a.some((log) => log.type === LOG_TYPE.ORPHAN_DELETE_ERROR);
2395
+ }).reduce((acc2, row) => {
2396
+ return acc2.concat(row.errorLogs);
2397
+ }, [])
2398
+ });
2399
+ }
2400
+ return acc;
2401
+ }, []);
2402
+ }
2403
+ getOrphanDeletedDetails() {
2404
+ return this.dataSets.reduce((acc, dataSet) => {
2405
+ if (dataSet.orphanBehavior) {
2406
+ acc.push({
2407
+ name: dataSet.name,
2408
+ hidden: dataSet.saveResultConfig.hidden,
2409
+ logs: dataSet.orphanData.filter((row) => row.status.orphanDeleted).reduce((acc2, row) => {
2410
+ return acc2.concat(row.errorLogs);
2411
+ }, [])
2412
+ });
2413
+ }
2414
+ return acc;
2415
+ }, []);
2416
+ }
2417
+ // Deprecated, will use getAllDetails instead
2030
2418
  getSaveDetails() {
2031
2419
  return this.dataSets.map((dataSet) => {
2032
2420
  var _a;
2033
2421
  const returnDetail = (_a = dataSet.saveResultConfig) == null ? void 0 : _a.returnDetail;
2034
- return {
2422
+ const result = {
2035
2423
  name: dataSet.name,
2036
2424
  hidden: dataSet.saveResultConfig.hidden,
2037
2425
  details: {
@@ -2039,140 +2427,89 @@ class SaveResult {
2039
2427
  inserted: (returnDetail == null ? void 0 : returnDetail.inserted) ? dataSet.data.filter((row) => row.status.inserted) : null,
2040
2428
  skipped: (returnDetail == null ? void 0 : returnDetail.skipped) ? dataSet.data.filter((row) => row.status.skipped) : null,
2041
2429
  updated: (returnDetail == null ? void 0 : returnDetail.updated) ? dataSet.data.filter((row) => row.status.updated) : null
2042
- }
2430
+ },
2431
+ orphanDetails: null
2043
2432
  };
2433
+ if (dataSet.orphanBehavior) {
2434
+ result.orphanDetails = {
2435
+ deleted: (returnDetail == null ? void 0 : returnDetail.orphanDeleted) ? dataSet.orphanData.filter((row) => row.status.orphanDeleted) : [],
2436
+ failed: dataSet.orphanData.filter((row) => {
2437
+ var _a2;
2438
+ return (_a2 = row.errorLogs) == null ? void 0 : _a2.some((log) => log.type === LOG_TYPE.ORPHAN_DELETE_ERROR);
2439
+ })
2440
+ };
2441
+ }
2442
+ return result;
2044
2443
  });
2045
2444
  }
2445
+ // Deprecated, will use getAllSummary instead
2046
2446
  getSaveSummary() {
2047
- return this.dataSets.map((dataSet) => ({
2048
- name: dataSet.name,
2049
- hidden: dataSet.saveResultConfig.hidden,
2050
- summary: {
2051
- total: dataSet.data.length,
2052
- failed: dataSet.data.filter((row) => row.status.failed).length,
2053
- inserted: dataSet.data.filter((row) => row.status.inserted).length,
2054
- skipped: dataSet.data.filter((row) => row.status.skipped).length,
2055
- updated: dataSet.data.filter((row) => row.status.updated).length
2447
+ return this.dataSets.map((dataSet) => {
2448
+ const result = {
2449
+ name: dataSet.name,
2450
+ hidden: dataSet.saveResultConfig.hidden,
2451
+ summary: {
2452
+ total: dataSet.data.length,
2453
+ failed: dataSet.data.filter((row) => row.status.failed).length,
2454
+ inserted: dataSet.data.filter((row) => row.status.inserted).length,
2455
+ skipped: dataSet.data.filter((row) => row.status.skipped).length,
2456
+ updated: dataSet.data.filter((row) => row.status.updated).length
2457
+ },
2458
+ orphanSummary: null
2459
+ };
2460
+ if (dataSet.orphanBehavior) {
2461
+ result.orphanSummary = {
2462
+ total: dataSet.orphanData.length,
2463
+ deleted: dataSet.orphanData.filter((row) => row.status.orphanDeleted).length,
2464
+ failed: dataSet.orphanData.filter((row) => {
2465
+ var _a;
2466
+ return (_a = row.errorLogs) == null ? void 0 : _a.some((log) => log.type === LOG_TYPE.ORPHAN_DELETE_ERROR);
2467
+ }).length
2468
+ };
2056
2469
  }
2057
- }));
2058
- }
2059
- }
2060
- class DataSet {
2061
- constructor(opts) {
2062
- opts = opts || {};
2063
- this.name = opts.name || "";
2064
- this.data = opts.data || [];
2065
- this.checkResultConfig = opts.checkResultConfig ? { ...opts.checkResultConfig } : {};
2066
- this.checkResultConfig.hidden = this.checkResultConfig.hidden ?? false;
2067
- this.saveResultConfig = opts.saveResultConfig ? { ...opts.saveResultConfig } : {};
2068
- this.saveResultConfig.hidden = this.saveResultConfig.hidden ?? false;
2069
- }
2070
- add(data) {
2071
- this.data.push(data);
2072
- return this;
2073
- }
2074
- getExistedData() {
2075
- return this.data.filter((item) => {
2076
- var _a;
2077
- return item.isValid() && ((_a = item.status) == null ? void 0 : _a.existed);
2078
- });
2079
- }
2080
- getInvalidData() {
2081
- return this.data.filter((item) => !item.isValid());
2082
- }
2083
- getNotExistedData() {
2084
- return this.data.filter((item) => {
2085
- var _a;
2086
- return item.isValid() && !((_a = item.status) == null ? void 0 : _a.existed);
2470
+ return result;
2087
2471
  });
2088
2472
  }
2089
- }
2090
- const DATAWRAPPER_STATUS = {
2091
- EXISTED: "existed",
2092
- FAILED: "failed",
2093
- INSERTED: "inserted",
2094
- SKIPPED: "skipped",
2095
- UPDATED: "updated"
2096
- };
2097
- class DataWrapper {
2098
- constructor(opts) {
2099
- this.data = opts.data || {};
2100
- this.errorLogs = opts.errorLogs || [];
2101
- this.ignoreLog = opts.ignoreLog;
2102
- this.rawData = opts.rawData || {};
2103
- this.rowIndex = opts.rowIndex || 0;
2104
- this.status = opts.status || {
2105
- existed: null,
2106
- failed: null,
2107
- inserted: null,
2108
- updated: null
2473
+ getAllSummary() {
2474
+ return {
2475
+ summary: this.getSummary(),
2476
+ orphanSummary: this.getOrphanSummary()
2109
2477
  };
2110
- this.transformedData = opts.transformedData || {};
2111
2478
  }
2112
- addError(detail) {
2113
- this.errorLogs.push(detail);
2114
- return this;
2115
- }
2116
- addInsertError(msg) {
2117
- this.addError({
2118
- rowIndex: this.rowIndex,
2119
- column: "",
2120
- message: msg,
2121
- type: "INSERT_ERROR"
2122
- });
2123
- return this;
2124
- }
2125
- addUpdateError(msg) {
2126
- this.addError({
2127
- rowIndex: this.rowIndex,
2128
- column: "",
2129
- message: msg,
2130
- type: "UPDATE_ERROR"
2479
+ getSummary() {
2480
+ return this.dataSets.map((dataSet) => {
2481
+ const result = {
2482
+ name: dataSet.name,
2483
+ hidden: dataSet.saveResultConfig.hidden,
2484
+ summary: {
2485
+ total: dataSet.data.length,
2486
+ failed: dataSet.data.filter((row) => row.status.failed).length,
2487
+ inserted: dataSet.data.filter((row) => row.status.inserted).length,
2488
+ skipped: dataSet.data.filter((row) => row.status.skipped).length,
2489
+ updated: dataSet.data.filter((row) => row.status.updated).length
2490
+ }
2491
+ };
2492
+ return result;
2131
2493
  });
2132
- return this;
2133
- }
2134
- isValid() {
2135
- return this.errorLogs.length === 0 && !this.ignoreLog;
2136
- }
2137
- setIgnoreLog(detail) {
2138
- this.ignoreLog = detail;
2139
- return this;
2140
- }
2141
- setData(data) {
2142
- this.data = data;
2143
- return this;
2144
- }
2145
- setExisted() {
2146
- this.setStatus("EXISTED");
2147
- return this;
2148
- }
2149
- setFailed() {
2150
- this.setStatus("FAILED");
2151
- return this;
2152
- }
2153
- setInserted() {
2154
- this.setStatus("INSERTED");
2155
- return this;
2156
- }
2157
- setRawData(rawData) {
2158
- this.rawData = rawData;
2159
- return this;
2160
- }
2161
- setSkipped() {
2162
- this.setStatus("SKIPPED");
2163
- return this;
2164
- }
2165
- setStatus(status) {
2166
- this.status[DATAWRAPPER_STATUS[status]] = Date.now();
2167
- return this;
2168
2494
  }
2169
- setTransformedData(transformedData) {
2170
- this.transformedData = transformedData;
2171
- return this;
2172
- }
2173
- setUpdated() {
2174
- this.setStatus("UPDATED");
2175
- return this;
2495
+ getOrphanSummary() {
2496
+ return this.dataSets.reduce((acc, dataSet) => {
2497
+ if (dataSet.orphanBehavior) {
2498
+ acc.push({
2499
+ name: dataSet.name,
2500
+ hidden: dataSet.saveResultConfig.hidden,
2501
+ summary: {
2502
+ total: dataSet.orphanData.length,
2503
+ deleted: dataSet.orphanData.filter((row) => row.status.orphanDeleted).length,
2504
+ failed: dataSet.orphanData.filter((row) => {
2505
+ var _a;
2506
+ return (_a = row.errorLogs) == null ? void 0 : _a.some((log) => log.type === LOG_TYPE.ORPHAN_DELETE_ERROR);
2507
+ }).length
2508
+ }
2509
+ });
2510
+ }
2511
+ return acc;
2512
+ }, []);
2176
2513
  }
2177
2514
  }
2178
2515
  class ExcelImportHelper {
@@ -2188,7 +2525,7 @@ class ExcelImportHelper {
2188
2525
  if (!worksheetData) continue;
2189
2526
  const worksheet = config.worksheets.find((item) => item.name === worksheetName);
2190
2527
  const dataSources = (worksheet == null ? void 0 : worksheet.dataSources) || [];
2191
- const { headerRow = 1, dataStartRow = 2 } = worksheet || {};
2528
+ const { headerRow = 1, dataStartRow = 2, rowIndexKey } = worksheet || {};
2192
2529
  for (const dataSource of dataSources) {
2193
2530
  if (!dataSource.headerConfigs || !dataSource.name) continue;
2194
2531
  const seenPrimaryKeys = /* @__PURE__ */ new Map();
@@ -2201,9 +2538,10 @@ class ExcelImportHelper {
2201
2538
  for (let rowIndex = 0; rowIndex < worksheetData.length; rowIndex++) {
2202
2539
  const row = worksheetData[rowIndex];
2203
2540
  const rawData = this.getRawDataFromHeaders(row, dataSource.headerConfigs);
2204
- let excelRow = this.createExcelRow(rawData, rowIndex + dataStartRow);
2541
+ let excelRow = this.createExcelRow(rawData, getRowIndex(row, rowIndexKey));
2205
2542
  const ctx = Object.assign({ dataSets: result.dataSets }, context);
2206
2543
  const handlers = [
2544
+ (row2) => this.checkRequired(row2, dataSource.headerConfigs, headers),
2207
2545
  (row2) => this.checkDuplicated(row2, dataSource, seenPrimaryKeys),
2208
2546
  (row2) => this.isIgnore(row2, ctx, dataSource.isIgnore),
2209
2547
  (row2) => this.transform(row2, dataSource.headerConfigs, headers),
@@ -2211,7 +2549,7 @@ class ExcelImportHelper {
2211
2549
  ];
2212
2550
  for (const handler of handlers) {
2213
2551
  excelRow = handler(excelRow);
2214
- if (excelRow.errorLogs.length > 0 || excelRow.ignoreLog) {
2552
+ if (excelRow.errorLogs.length > 0 || excelRow.ignoreLog || excelRow.isDuplicated()) {
2215
2553
  break;
2216
2554
  }
2217
2555
  }
@@ -2225,10 +2563,9 @@ class ExcelImportHelper {
2225
2563
  }
2226
2564
  static createExcelRow(row, rowIndex) {
2227
2565
  return new DataWrapper({
2228
- // data: { ...row },
2229
2566
  rawData: row,
2230
2567
  rowIndex,
2231
- errorLogs: []
2568
+ logs: []
2232
2569
  });
2233
2570
  }
2234
2571
  static getRawDataFromHeaders(excelRow, headerConfigs = []) {
@@ -2244,25 +2581,28 @@ class ExcelImportHelper {
2244
2581
  }
2245
2582
  const { ignore, reason } = isIgnore({ rawData: row.rawData, rowIndex: row.rowIndex, ctx });
2246
2583
  if (ignore) {
2247
- row.setIgnoreLog({
2248
- rowIndex: row.rowIndex,
2249
- column: "",
2250
- message: reason || "",
2251
- type: "IGNORE"
2252
- });
2584
+ row.addIgnoreLog(reason || "");
2585
+ }
2586
+ return row;
2587
+ }
2588
+ static checkRequired(row, headerConfigs = [], headers = []) {
2589
+ const rawData = { ...row.rawData };
2590
+ for (const config of headerConfigs) {
2591
+ const value = rawData[config.name];
2592
+ if (config.required && (value === null || value === void 0 || value === "")) {
2593
+ row.addError({
2594
+ column: getExcelColumnName(headers.indexOf(config.name)),
2595
+ message: `${config.name} is required.`,
2596
+ type: LOG_TYPE.REQUIRED_ERROR
2597
+ });
2598
+ }
2253
2599
  }
2254
2600
  return row;
2255
2601
  }
2256
2602
  static checkDuplicated(row, dataSource, seenPrimaryKeys) {
2257
2603
  const primaryKey = dataSource.primaryKeys.map((key) => row.rawData[key]).join("-");
2258
2604
  if (seenPrimaryKeys.has(primaryKey)) {
2259
- row.addError({
2260
- rowIndex: row.rowIndex,
2261
- // column: getPrimaryKeyColumn(dataSource.primaryKeys, headers),
2262
- column: "",
2263
- message: `Duplicated with row ${seenPrimaryKeys.get(primaryKey) || 0}`,
2264
- type: "DUPLICATED"
2265
- });
2605
+ row.addDuplicatedLog(`Duplicated with row ${seenPrimaryKeys.get(primaryKey) || 0}.`);
2266
2606
  } else {
2267
2607
  seenPrimaryKeys.set(primaryKey, row.rowIndex);
2268
2608
  }
@@ -2272,26 +2612,13 @@ class ExcelImportHelper {
2272
2612
  const rawData = { ...row.rawData };
2273
2613
  const transformedData = {};
2274
2614
  for (const config of headerConfigs) {
2275
- const value = rawData[config.name];
2276
- let hasRequiredError = false;
2277
- if (config.required && (value === null || value === void 0 || value === "")) {
2278
- row.addError({
2279
- rowIndex: row.rowIndex,
2280
- column: getExcelColumnName(headers.indexOf(config.name)),
2281
- message: `${config.name} is required`,
2282
- type: "REQUIRED"
2283
- });
2284
- hasRequiredError = true;
2285
- }
2286
- if (hasRequiredError) continue;
2287
2615
  try {
2288
2616
  transformedData[config.name] = typeof config.transform === "function" ? config.transform(rawData[config.name]) : rawData[config.name];
2289
2617
  } catch (error) {
2290
2618
  row.addError({
2291
- rowIndex: row.rowIndex,
2292
2619
  column: getExcelColumnName(headers.indexOf(config.name)),
2293
- message: `transform ${config.name} failed`,
2294
- type: "TRANSFORM"
2620
+ message: `Transform ${config.name} failed: ${error.message}`,
2621
+ type: LOG_TYPE.TRANSFORM_ERROR
2295
2622
  });
2296
2623
  }
2297
2624
  }
@@ -2309,10 +2636,9 @@ class ExcelImportHelper {
2309
2636
  return row.setData(result);
2310
2637
  } catch (error) {
2311
2638
  row.addError({
2312
- rowIndex: row.rowIndex,
2313
2639
  column: "",
2314
- message: `convert failed`,
2315
- type: "CONVERT"
2640
+ message: `Convert failed: ${error.message}`,
2641
+ type: LOG_TYPE.CONVERT_ERROR
2316
2642
  });
2317
2643
  }
2318
2644
  return row;
@@ -2338,6 +2664,9 @@ class ExcelImportHelper {
2338
2664
  if (checkExisted && typeof checkExisted === "function") {
2339
2665
  updatedDataSet = await checkExisted({ dataSet: updatedDataSet, ctx });
2340
2666
  }
2667
+ if (dataSource.orphanBehavior === "DELETE" && dataSource.checkOrphans && typeof dataSource.checkOrphans === "function") {
2668
+ await dataSource.checkOrphans({ dataSet: updatedDataSet, ctx });
2669
+ }
2341
2670
  if (handleData && typeof handleData === "function") {
2342
2671
  updatedDataSet = await handleData({ dataSet: updatedDataSet, dataSource, ctx });
2343
2672
  } else {
@@ -2360,6 +2689,9 @@ function getExcelColumnName(columnIndex) {
2360
2689
  }
2361
2690
  return result;
2362
2691
  }
2692
+ function getRowIndex(row, rowIndexKey) {
2693
+ return row[rowIndexKey || "__rowNum"] || 0;
2694
+ }
2363
2695
  function customHandler({ responseHelper, handler, ignoreError = false }) {
2364
2696
  return async (reqOrCtx, resOrNext, nextInExpress) => {
2365
2697
  const isKoa = typeof resOrNext === "function" && !nextInExpress;
@@ -2622,6 +2954,26 @@ class Repo {
2622
2954
  }
2623
2955
  return options;
2624
2956
  }
2957
+ async bulkWriteDocs({ operations, config, systemLog }) {
2958
+ const log = _makeLog({
2959
+ systemLog,
2960
+ label: "REPO_BULK_WRITE",
2961
+ message: `fn ${this._classname}.prototype.bulkWriteDocs`,
2962
+ input: [{ operations: { ...operations }, config: { ...config }, systemLog: { ...systemLog } }]
2963
+ });
2964
+ const sharedOptions = {
2965
+ ...this._sharedOptions,
2966
+ ...config
2967
+ };
2968
+ try {
2969
+ const result = await this.model.bulkWriteDocs(operations, sharedOptions);
2970
+ log({ level: "info", output: { ...result } });
2971
+ return result;
2972
+ } catch (err) {
2973
+ log({ level: "warn", output: err.toString() });
2974
+ throw err;
2975
+ }
2976
+ }
2625
2977
  async deleteOne({ id }) {
2626
2978
  try {
2627
2979
  const result = await this.model.deleteOne({ _id: id });
@@ -2905,6 +3257,27 @@ function makeService({ repo }) {
2905
3257
  }
2906
3258
  return new Service({ repo });
2907
3259
  }
3260
+ class ServiceBulkWrite extends Service {
3261
+ constructor({ repo }) {
3262
+ super({ repo });
3263
+ }
3264
+ static get _classname() {
3265
+ return "ServiceBulkWrite";
3266
+ }
3267
+ async bulkWriteDocs({ operations, config }) {
3268
+ const result = await this.repo.bulkWriteDocs({ operations, config });
3269
+ return result;
3270
+ }
3271
+ }
3272
+ function makeServiceBulkWrite({ repo }) {
3273
+ if (repo === void 0) {
3274
+ throw new Error("repo is required.");
3275
+ }
3276
+ if (repo._superclass !== Repo._superclass) {
3277
+ throw new Error("repo is not an instance of Repo.");
3278
+ }
3279
+ return new ServiceBulkWrite({ repo });
3280
+ }
2908
3281
  async function generalPost({ body = {}, GeneralModel, UniqueKeyGenerator: UniqueKeyGenerator2, resourceInfo }) {
2909
3282
  const { resources, data, globalShared = {}, shared = {}, relationship = {} } = body;
2910
3283
  const _resourceInfo = resourceInfo || body.resourceInfo;
@@ -3025,27 +3398,6 @@ function groupArrayByKey(arr, key) {
3025
3398
  return acc;
3026
3399
  }, {});
3027
3400
  }
3028
- function init(_class, options) {
3029
- if (options instanceof _class) {
3030
- return options;
3031
- }
3032
- try {
3033
- const instance2 = new _class(options);
3034
- return instance2.isValid !== false ? instance2 : null;
3035
- } catch (e) {
3036
- console.log(`init failed for class: ${_class._classname || "no _classname"}`, e);
3037
- return null;
3038
- }
3039
- }
3040
- function initFromArray(_class, arr) {
3041
- if (Array.isArray(arr)) {
3042
- return arr.map((a) => init(_class, a));
3043
- }
3044
- return [];
3045
- }
3046
- function initOnlyValidFromArray(_class, arr) {
3047
- return initFromArray(_class, arr).filter((i) => i);
3048
- }
3049
3401
  function mergeArraysByKey(arr1, arr2) {
3050
3402
  const safeArr1 = Array.isArray(arr1) ? arr1 : [];
3051
3403
  const safeArr2 = Array.isArray(arr2) ? arr2 : [];
@@ -3636,6 +3988,7 @@ const helpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
3636
3988
  __proto__: null,
3637
3989
  CheckResult,
3638
3990
  ExcelImportHelper,
3991
+ LOG_TYPE,
3639
3992
  authorize,
3640
3993
  calculateAge,
3641
3994
  changeCreatorOwner,
@@ -4751,6 +5104,7 @@ const models = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
4751
5104
  QMeta,
4752
5105
  Repo,
4753
5106
  Service,
5107
+ ServiceBulkWrite,
4754
5108
  Status,
4755
5109
  StatusDocument,
4756
5110
  TemplateCompiler,
@@ -4761,7 +5115,8 @@ const models = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
4761
5115
  WebAppResponse,
4762
5116
  getQLogEnv,
4763
5117
  makeApiResponse,
4764
- makeService
5118
+ makeService,
5119
+ makeServiceBulkWrite
4765
5120
  }, Symbol.toStringTag, { value: "Module" }));
4766
5121
  const index = {
4767
5122
  ...helpers,
@@ -4776,6 +5131,7 @@ exports.ERROR_CODES = ERROR_CODES;
4776
5131
  exports.ErrorDetail = ErrorDetail;
4777
5132
  exports.ExcelImportHelper = ExcelImportHelper;
4778
5133
  exports.KeyValueObject = KeyValueObject;
5134
+ exports.LOG_TYPE = LOG_TYPE;
4779
5135
  exports.LogRecord = LogRecord;
4780
5136
  exports.Metadata = Metadata;
4781
5137
  exports.PushEnvelope = PushEnvelope;
@@ -4785,6 +5141,7 @@ exports.QLogLevel = QLogLevel;
4785
5141
  exports.QMeta = QMeta;
4786
5142
  exports.Repo = Repo;
4787
5143
  exports.Service = Service;
5144
+ exports.ServiceBulkWrite = ServiceBulkWrite;
4788
5145
  exports.Status = Status;
4789
5146
  exports.StatusDocument = StatusDocument;
4790
5147
  exports.TemplateCompiler = TemplateCompiler;
@@ -4819,6 +5176,7 @@ exports.initOnlyValidFromArray = initOnlyValidFromArray;
4819
5176
  exports.isConvertibleToNumber = isConvertibleToNumber;
4820
5177
  exports.makeApiResponse = makeApiResponse;
4821
5178
  exports.makeService = makeService;
5179
+ exports.makeServiceBulkWrite = makeServiceBulkWrite;
4822
5180
  exports.mergeArraysByKey = mergeArraysByKey;
4823
5181
  exports.objectHelper = objectHelper;
4824
5182
  exports.pMap = pMap;