@questwork/q-utilities 0.1.34 → 0.1.36

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