@nocobase/plugin-action-import 2.0.0-alpha.8 → 2.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -66,6 +66,17 @@ class XlsxImporter extends import_events.default {
66
66
  async beforePerformImport(data, options) {
67
67
  return data;
68
68
  }
69
+ async validateBySpaces(data, ctx) {
70
+ var _a;
71
+ if ((_a = ctx == null ? void 0 : ctx.space) == null ? void 0 : _a.can) {
72
+ await ctx.space.can({
73
+ data: (data == null ? void 0 : data.slice(1)) || [],
74
+ columns: this.options.columns.map((column) => column.dataIndex),
75
+ collection: this.options.collection.name,
76
+ ctx
77
+ });
78
+ }
79
+ }
69
80
  async validate(ctx) {
70
81
  const columns = this.getColumnsByPermission(ctx);
71
82
  if (columns.length == 0) {
@@ -78,6 +89,7 @@ class XlsxImporter extends import_events.default {
78
89
  }
79
90
  }
80
91
  const data = await this.getData(ctx);
92
+ await this.validateBySpaces(data, ctx);
81
93
  return data;
82
94
  }
83
95
  async run(options = {}) {
@@ -187,12 +199,13 @@ class XlsxImporter extends import_events.default {
187
199
  getModel() {
188
200
  return this.repository instanceof import_database.RelationRepository ? this.repository.targetModel : this.repository.model;
189
201
  }
190
- async handleRowValuesWithColumns(row, rowValues, options) {
191
- for (let index = 0; index < this.options.columns.length; index++) {
192
- const column = this.options.columns[index];
202
+ async handleRowValuesWithColumns(row, rowValues, options, columns) {
203
+ var _a;
204
+ for (let index = 0; index < columns.length; index++) {
205
+ const column = columns[index];
193
206
  const field = this.options.collection.getField(column.dataIndex[0]);
194
207
  if (!field) {
195
- throw new import_errors.ImportValidationError("Import validation.Field not found", {
208
+ throw new import_errors.ImportValidationError("Import validation. Field not found", {
196
209
  field: column.dataIndex[0]
197
210
  });
198
211
  }
@@ -215,7 +228,15 @@ class XlsxImporter extends import_events.default {
215
228
  ctx.targetCollection = field.targetCollection();
216
229
  ctx.filterKey = column.dataIndex[1];
217
230
  }
218
- rowValues[dataKey] = await interfaceInstance.toValue(this.trimString(str), ctx);
231
+ try {
232
+ rowValues[dataKey] = str == null ? null : await interfaceInstance.toValue(this.trimString(str), ctx);
233
+ } catch (error) {
234
+ throw new import_errors.ImportValidationError("Failed to parse field {{field}} in row {{rowIndex}}: {{message}}", {
235
+ rowIndex: ((_a = options == null ? void 0 : options.context) == null ? void 0 : _a.handingRowIndex) || 1,
236
+ field: dataKey,
237
+ message: error.message
238
+ });
239
+ }
219
240
  }
220
241
  const model = this.getModel();
221
242
  const guard = import_database.UpdateGuard.fromOptions(model, {
@@ -226,13 +247,14 @@ class XlsxImporter extends import_events.default {
226
247
  rowValues = this.repository instanceof import_database.RelationRepository ? model.callSetters(guard.sanitize(rowValues || {}), options) : guard.sanitize(rowValues);
227
248
  }
228
249
  async handleChuckRows(chunkRows, runOptions, options) {
229
- var _a, _b;
250
+ var _a, _b, _c;
230
251
  let { handingRowIndex = 1 } = options;
231
252
  const { transaction } = runOptions;
253
+ const columns = this.getColumnsByPermission(options == null ? void 0 : options.context);
232
254
  const rows = [];
233
255
  for (const row of chunkRows) {
234
256
  const rowValues = {};
235
- await this.handleRowValuesWithColumns(row, rowValues, runOptions);
257
+ await this.handleRowValuesWithColumns(row, rowValues, runOptions, columns);
236
258
  rows.push({
237
259
  ...this.options.rowDefaultValues || {},
238
260
  ...rowValues
@@ -257,14 +279,18 @@ class XlsxImporter extends import_events.default {
257
279
  )}`
258
280
  );
259
281
  }
260
- (_b = this.logger) == null ? void 0 : _b.error(`Import error at row ${handingRowIndex}: ${error.message}`, {
282
+ if ((_b = error.params) == null ? void 0 : _b.rowIndex) {
283
+ handingRowIndex += error.params.rowIndex;
284
+ error.params.rowIndex = handingRowIndex;
285
+ }
286
+ (_c = this.logger) == null ? void 0 : _c.error(`Import error at row ${handingRowIndex}: ${error.message}`, {
261
287
  rowIndex: handingRowIndex,
262
288
  rowData: rows[handingRowIndex],
263
289
  originalError: error.stack || error.toString()
264
290
  });
265
291
  throw new import_errors.ImportError(`Import failed at row ${handingRowIndex}`, {
266
292
  rowIndex: handingRowIndex,
267
- rowData: rows[handingRowIndex],
293
+ rowData: rows[handingRowIndex - (this.options.explain ? 2 : 1)],
268
294
  cause: error
269
295
  });
270
296
  }
@@ -288,15 +314,10 @@ class XlsxImporter extends import_events.default {
288
314
  for (let i = 0; i < instances.length; i++) {
289
315
  const instance = instances[i];
290
316
  const value = values[i];
291
- await this.loggerService.measureExecutedTime(
292
- async () => (0, import_database.updateAssociations)(instance, value, { transaction }),
293
- `Row ${i + 1}: updateAssociations completed in {time}ms`,
294
- "debug"
295
- );
296
317
  if (insertOptions.hooks !== false) {
297
318
  await this.loggerService.measureExecutedTime(
298
319
  async () => {
299
- await db.emit(`${this.repository.collection.name}.afterCreate`, instance, {
320
+ await db.emitAsync(`${this.repository.collection.name}.afterCreate`, instance, {
300
321
  transaction
301
322
  });
302
323
  await db.emitAsync(`${this.repository.collection.name}.afterSave`, instance, {
@@ -308,6 +329,11 @@ class XlsxImporter extends import_events.default {
308
329
  "debug"
309
330
  );
310
331
  }
332
+ await this.loggerService.measureExecutedTime(
333
+ async () => (0, import_database.updateAssociations)(instance, value, { transaction }),
334
+ `Row ${i + 1}: updateAssociations completed in {time}ms`,
335
+ "debug"
336
+ );
311
337
  if ((context == null ? void 0 : context.skipWorkflow) !== true) {
312
338
  await this.loggerService.measureExecutedTime(
313
339
  async () => {
@@ -378,7 +404,7 @@ class XlsxImporter extends import_events.default {
378
404
  headers: expectedHeaders.join(", ")
379
405
  });
380
406
  }
381
- data = this.alignWithHeaders({ data, expectedHeaders, headers });
407
+ data = this.alignWithHeaders({ data, expectedHeaders, headerRowIndex });
382
408
  const rows = data.slice(headerRowIndex + 1);
383
409
  if (rows.length === 0) {
384
410
  throw new import_errors.ImportValidationError("No data to import");
@@ -386,8 +412,14 @@ class XlsxImporter extends import_events.default {
386
412
  return [headers, ...rows];
387
413
  }
388
414
  alignWithHeaders(params) {
389
- const { expectedHeaders, headers, data } = params;
390
- const keepCols = headers.map((x, i) => expectedHeaders.includes(x) ? i : -1).filter((i) => i > -1);
415
+ const { data, expectedHeaders, headerRowIndex } = params;
416
+ const headerRow = data[headerRowIndex];
417
+ const keepCols = expectedHeaders.map((header) => headerRow.indexOf(header));
418
+ if (keepCols.some((index) => index < 0)) {
419
+ throw new import_errors.ImportValidationError("Headers not found. Expected headers: {{headers}}", {
420
+ headers: expectedHeaders.join(", ")
421
+ });
422
+ }
391
423
  return data.map((row) => keepCols.map((i) => row[i]));
392
424
  }
393
425
  findAndValidateHeaders(options) {
@@ -401,6 +433,7 @@ class XlsxImporter extends import_events.default {
401
433
  return { headerRowIndex: rowIndex, headers: orderedHeaders };
402
434
  }
403
435
  }
436
+ return { headerRowIndex: -1, headers: [] };
404
437
  }
405
438
  }
406
439
  // Annotate the CommonJS export names for ESM import in node:
package/package.json CHANGED
@@ -2,12 +2,15 @@
2
2
  "name": "@nocobase/plugin-action-import",
3
3
  "displayName": "Action: Import records",
4
4
  "displayName.zh-CN": "操作:导入记录",
5
+ "displayName.ru-RU": "Действие: Импорт записей",
5
6
  "description": "Import records using excel templates. You can configure which fields to import and templates will be generated automatically.",
7
+ "description.ru-RU": "Импорт записей с помощью шаблонов Excel: можно настроить, какие поля импортировать, шаблоны будут генерироваться автоматически.",
6
8
  "description.zh-CN": "使用 Excel 模板导入数据,可以配置导入哪些字段,自动生成模板。",
7
- "version": "2.0.0-alpha.8",
9
+ "version": "2.1.0-alpha.1",
8
10
  "license": "AGPL-3.0",
9
11
  "main": "./dist/server/index.js",
10
12
  "homepage": "https://docs.nocobase.com/handbook/action-import",
13
+ "homepage.ru-RU": "https://docs.nocobase.ru/handbook/action-import",
11
14
  "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/action-import",
12
15
  "devDependencies": {
13
16
  "@ant-design/icons": "5.x",
@@ -36,7 +39,7 @@
36
39
  "@nocobase/test": "2.x",
37
40
  "@nocobase/utils": "2.x"
38
41
  },
39
- "gitHead": "ff91246a3914c72dc1f4000d85df10a16798ff78",
42
+ "gitHead": "d27baf21569643d6fa83f882233f4e90eb5b89f1",
40
43
  "keywords": [
41
44
  "Actions"
42
45
  ]