@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.
@@ -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();
@@ -2199,9 +2536,10 @@ class ExcelImportHelper {
2199
2536
  for (let rowIndex = 0; rowIndex < worksheetData.length; rowIndex++) {
2200
2537
  const row = worksheetData[rowIndex];
2201
2538
  const rawData = this.getRawDataFromHeaders(row, dataSource.headerConfigs);
2202
- let excelRow = this.createExcelRow(rawData, rowIndex + dataStartRow);
2539
+ let excelRow = this.createExcelRow(rawData, getRowIndex(row, rowIndexKey));
2203
2540
  const ctx = Object.assign({ dataSets: result.dataSets }, context);
2204
2541
  const handlers = [
2542
+ (row2) => this.checkRequired(row2, dataSource.headerConfigs, headers),
2205
2543
  (row2) => this.checkDuplicated(row2, dataSource, seenPrimaryKeys),
2206
2544
  (row2) => this.isIgnore(row2, ctx, dataSource.isIgnore),
2207
2545
  (row2) => this.transform(row2, dataSource.headerConfigs, headers),
@@ -2209,7 +2547,7 @@ class ExcelImportHelper {
2209
2547
  ];
2210
2548
  for (const handler of handlers) {
2211
2549
  excelRow = handler(excelRow);
2212
- if (excelRow.errorLogs.length > 0 || excelRow.ignoreLog) {
2550
+ if (excelRow.errorLogs.length > 0 || excelRow.ignoreLog || excelRow.isDuplicated()) {
2213
2551
  break;
2214
2552
  }
2215
2553
  }
@@ -2223,10 +2561,9 @@ class ExcelImportHelper {
2223
2561
  }
2224
2562
  static createExcelRow(row, rowIndex) {
2225
2563
  return new DataWrapper({
2226
- // data: { ...row },
2227
2564
  rawData: row,
2228
2565
  rowIndex,
2229
- errorLogs: []
2566
+ logs: []
2230
2567
  });
2231
2568
  }
2232
2569
  static getRawDataFromHeaders(excelRow, headerConfigs = []) {
@@ -2242,25 +2579,28 @@ class ExcelImportHelper {
2242
2579
  }
2243
2580
  const { ignore, reason } = isIgnore({ rawData: row.rawData, rowIndex: row.rowIndex, ctx });
2244
2581
  if (ignore) {
2245
- row.setIgnoreLog({
2246
- rowIndex: row.rowIndex,
2247
- column: "",
2248
- message: reason || "",
2249
- type: "IGNORE"
2250
- });
2582
+ row.addIgnoreLog(reason || "");
2583
+ }
2584
+ return row;
2585
+ }
2586
+ static checkRequired(row, headerConfigs = [], headers = []) {
2587
+ const rawData = { ...row.rawData };
2588
+ for (const config of headerConfigs) {
2589
+ const value = rawData[config.name];
2590
+ if (config.required && (value === null || value === void 0 || value === "")) {
2591
+ row.addError({
2592
+ column: getExcelColumnName(headers.indexOf(config.name)),
2593
+ message: `${config.name} is required.`,
2594
+ type: LOG_TYPE.REQUIRED_ERROR
2595
+ });
2596
+ }
2251
2597
  }
2252
2598
  return row;
2253
2599
  }
2254
2600
  static checkDuplicated(row, dataSource, seenPrimaryKeys) {
2255
2601
  const primaryKey = dataSource.primaryKeys.map((key) => row.rawData[key]).join("-");
2256
2602
  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
- });
2603
+ row.addDuplicatedLog(`Duplicated with row ${seenPrimaryKeys.get(primaryKey) || 0}.`);
2264
2604
  } else {
2265
2605
  seenPrimaryKeys.set(primaryKey, row.rowIndex);
2266
2606
  }
@@ -2270,26 +2610,13 @@ class ExcelImportHelper {
2270
2610
  const rawData = { ...row.rawData };
2271
2611
  const transformedData = {};
2272
2612
  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
2613
  try {
2286
2614
  transformedData[config.name] = typeof config.transform === "function" ? config.transform(rawData[config.name]) : rawData[config.name];
2287
2615
  } catch (error) {
2288
2616
  row.addError({
2289
- rowIndex: row.rowIndex,
2290
2617
  column: getExcelColumnName(headers.indexOf(config.name)),
2291
- message: `transform ${config.name} failed`,
2292
- type: "TRANSFORM"
2618
+ message: `Transform ${config.name} failed: ${error.message}`,
2619
+ type: LOG_TYPE.TRANSFORM_ERROR
2293
2620
  });
2294
2621
  }
2295
2622
  }
@@ -2307,10 +2634,9 @@ class ExcelImportHelper {
2307
2634
  return row.setData(result);
2308
2635
  } catch (error) {
2309
2636
  row.addError({
2310
- rowIndex: row.rowIndex,
2311
2637
  column: "",
2312
- message: `convert failed`,
2313
- type: "CONVERT"
2638
+ message: `Convert failed: ${error.message}`,
2639
+ type: LOG_TYPE.CONVERT_ERROR
2314
2640
  });
2315
2641
  }
2316
2642
  return row;
@@ -2336,6 +2662,9 @@ class ExcelImportHelper {
2336
2662
  if (checkExisted && typeof checkExisted === "function") {
2337
2663
  updatedDataSet = await checkExisted({ dataSet: updatedDataSet, ctx });
2338
2664
  }
2665
+ if (dataSource.orphanBehavior === "DELETE" && dataSource.checkOrphans && typeof dataSource.checkOrphans === "function") {
2666
+ await dataSource.checkOrphans({ dataSet: updatedDataSet, ctx });
2667
+ }
2339
2668
  if (handleData && typeof handleData === "function") {
2340
2669
  updatedDataSet = await handleData({ dataSet: updatedDataSet, dataSource, ctx });
2341
2670
  } else {
@@ -2358,6 +2687,9 @@ function getExcelColumnName(columnIndex) {
2358
2687
  }
2359
2688
  return result;
2360
2689
  }
2690
+ function getRowIndex(row, rowIndexKey) {
2691
+ return row[rowIndexKey || "__rowNum"] || 0;
2692
+ }
2361
2693
  function customHandler({ responseHelper, handler, ignoreError = false }) {
2362
2694
  return async (reqOrCtx, resOrNext, nextInExpress) => {
2363
2695
  const isKoa = typeof resOrNext === "function" && !nextInExpress;
@@ -2620,6 +2952,26 @@ class Repo {
2620
2952
  }
2621
2953
  return options;
2622
2954
  }
2955
+ async bulkWriteDocs({ operations, config, systemLog }) {
2956
+ const log = _makeLog({
2957
+ systemLog,
2958
+ label: "REPO_BULK_WRITE",
2959
+ message: `fn ${this._classname}.prototype.bulkWriteDocs`,
2960
+ input: [{ operations: { ...operations }, config: { ...config }, systemLog: { ...systemLog } }]
2961
+ });
2962
+ const sharedOptions = {
2963
+ ...this._sharedOptions,
2964
+ ...config
2965
+ };
2966
+ try {
2967
+ const result = await this.model.bulkWriteDocs(operations, sharedOptions);
2968
+ log({ level: "info", output: { ...result } });
2969
+ return result;
2970
+ } catch (err) {
2971
+ log({ level: "warn", output: err.toString() });
2972
+ throw err;
2973
+ }
2974
+ }
2623
2975
  async deleteOne({ id }) {
2624
2976
  try {
2625
2977
  const result = await this.model.deleteOne({ _id: id });
@@ -2903,6 +3255,27 @@ function makeService({ repo }) {
2903
3255
  }
2904
3256
  return new Service({ repo });
2905
3257
  }
3258
+ class ServiceBulkWrite extends Service {
3259
+ constructor({ repo }) {
3260
+ super({ repo });
3261
+ }
3262
+ static get _classname() {
3263
+ return "ServiceBulkWrite";
3264
+ }
3265
+ async bulkWriteDocs({ operations, config }) {
3266
+ const result = await this.repo.bulkWriteDocs({ operations, config });
3267
+ return result;
3268
+ }
3269
+ }
3270
+ function makeServiceBulkWrite({ repo }) {
3271
+ if (repo === void 0) {
3272
+ throw new Error("repo is required.");
3273
+ }
3274
+ if (repo._superclass !== Repo._superclass) {
3275
+ throw new Error("repo is not an instance of Repo.");
3276
+ }
3277
+ return new ServiceBulkWrite({ repo });
3278
+ }
2906
3279
  async function generalPost({ body = {}, GeneralModel, UniqueKeyGenerator: UniqueKeyGenerator2, resourceInfo }) {
2907
3280
  const { resources, data, globalShared = {}, shared = {}, relationship = {} } = body;
2908
3281
  const _resourceInfo = resourceInfo || body.resourceInfo;
@@ -3023,27 +3396,6 @@ function groupArrayByKey(arr, key) {
3023
3396
  return acc;
3024
3397
  }, {});
3025
3398
  }
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
3399
  function mergeArraysByKey(arr1, arr2) {
3048
3400
  const safeArr1 = Array.isArray(arr1) ? arr1 : [];
3049
3401
  const safeArr2 = Array.isArray(arr2) ? arr2 : [];
@@ -3634,6 +3986,7 @@ const helpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
3634
3986
  __proto__: null,
3635
3987
  CheckResult,
3636
3988
  ExcelImportHelper,
3989
+ LOG_TYPE,
3637
3990
  authorize,
3638
3991
  calculateAge,
3639
3992
  changeCreatorOwner,
@@ -4749,6 +5102,7 @@ const models = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
4749
5102
  QMeta,
4750
5103
  Repo,
4751
5104
  Service,
5105
+ ServiceBulkWrite,
4752
5106
  Status,
4753
5107
  StatusDocument,
4754
5108
  TemplateCompiler,
@@ -4759,7 +5113,8 @@ const models = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
4759
5113
  WebAppResponse,
4760
5114
  getQLogEnv,
4761
5115
  makeApiResponse,
4762
- makeService
5116
+ makeService,
5117
+ makeServiceBulkWrite
4763
5118
  }, Symbol.toStringTag, { value: "Module" }));
4764
5119
  const index = {
4765
5120
  ...helpers,
@@ -4775,6 +5130,7 @@ export {
4775
5130
  ErrorDetail,
4776
5131
  ExcelImportHelper,
4777
5132
  KeyValueObject,
5133
+ LOG_TYPE,
4778
5134
  LogRecord,
4779
5135
  Metadata,
4780
5136
  PushEnvelope,
@@ -4784,6 +5140,7 @@ export {
4784
5140
  QMeta,
4785
5141
  Repo,
4786
5142
  Service,
5143
+ ServiceBulkWrite,
4787
5144
  Status,
4788
5145
  StatusDocument,
4789
5146
  TemplateCompiler,
@@ -4818,6 +5175,7 @@ export {
4818
5175
  isConvertibleToNumber,
4819
5176
  makeApiResponse,
4820
5177
  makeService,
5178
+ makeServiceBulkWrite,
4821
5179
  mergeArraysByKey,
4822
5180
  objectHelper,
4823
5181
  pMap,