@haus-tech/product-import-export-plugin 3.6.11 → 3.6.13

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +65 -8
  3. package/package.json +1 -1
  4. package/src/constants.d.ts +1 -0
  5. package/src/constants.js +2 -1
  6. package/src/constants.js.map +1 -1
  7. package/src/index.d.ts +1 -0
  8. package/src/index.js +1 -0
  9. package/src/index.js.map +1 -1
  10. package/src/product-import-export.plugin.d.ts +11 -1
  11. package/src/product-import-export.plugin.js +32 -2
  12. package/src/product-import-export.plugin.js.map +1 -1
  13. package/src/providers/import-providers/import-parser.d.ts +4 -0
  14. package/src/providers/import-providers/import-parser.js +16 -7
  15. package/src/providers/import-providers/import-parser.js.map +1 -1
  16. package/src/providers/import-providers/product-importer.d.ts +43 -10
  17. package/src/providers/import-providers/product-importer.js +109 -66
  18. package/src/providers/import-providers/product-importer.js.map +1 -1
  19. package/src/services/extended-fast-importer.service.d.ts +7 -0
  20. package/src/services/extended-fast-importer.service.js +40 -36
  21. package/src/services/extended-fast-importer.service.js.map +1 -1
  22. package/src/services/import-row-groups.d.ts +17 -0
  23. package/src/services/import-row-groups.js +69 -0
  24. package/src/services/import-row-groups.js.map +1 -0
  25. package/src/services/product-import.service.d.ts +1 -3
  26. package/src/services/product-import.service.js +10 -9
  27. package/src/services/product-import.service.js.map +1 -1
  28. package/src/services/relation-lookup-resolver.d.ts +57 -0
  29. package/src/services/relation-lookup-resolver.js +272 -0
  30. package/src/services/relation-lookup-resolver.js.map +1 -0
  31. package/src/strategies/import-row-validation-strategy.d.ts +71 -0
  32. package/src/strategies/import-row-validation-strategy.js +16 -0
  33. package/src/strategies/import-row-validation-strategy.js.map +1 -0
  34. package/src/types.d.ts +8 -0
  35. package/src/services/sku-relation-resolver.d.ts +0 -33
  36. package/src/services/sku-relation-resolver.js +0 -177
  37. package/src/services/sku-relation-resolver.js.map +0 -1
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.splitSkuList = exports.RelationLookupResolver = exports.SKU_LOOKUP = void 0;
13
+ exports.relationLookupKey = relationLookupKey;
14
+ exports.splitLookupList = splitLookupList;
15
+ const common_1 = require("@nestjs/common");
16
+ const core_1 = require("@vendure/core");
17
+ const sync_1 = require("csv-parse/sync");
18
+ const typeorm_1 = require("typeorm");
19
+ const import_csv_lines_1 = require("../helpers/import-csv-lines");
20
+ /** The fourth header segment that makes a ProductVariant relation column hold SKUs. */
21
+ exports.SKU_LOOKUP = 'sku';
22
+ /** Values per database query, well below every driver's bind parameter limit. */
23
+ const LOOKUP_QUERY_CHUNK_SIZE = 1000;
24
+ /** Column types a lookup can match a CSV cell against without a cast. */
25
+ const TEXT_COLUMN_TYPES = new Set([
26
+ 'varchar',
27
+ 'character varying',
28
+ 'nvarchar',
29
+ 'text',
30
+ 'char',
31
+ 'character',
32
+ 'nchar',
33
+ 'citext',
34
+ ]);
35
+ /** The key of one looked-up value in `RelationLookupResult.idsByLookup`. */
36
+ function relationLookupKey(lookup, value) {
37
+ return `${lookup.entityName}:${lookup.column}:${value}`;
38
+ }
39
+ /**
40
+ * Resolves relation custom field columns written as `<owner>:<field>:<entity>:<column>`, where
41
+ * each value names the related row by that column instead of by id (`:sku` on a ProductVariant
42
+ * relation names the variant by SKU). Every value must match exactly one row that is not
43
+ * soft-deleted, in any channel: related rows usually live outside the channel being imported into.
44
+ */
45
+ let RelationLookupResolver = class RelationLookupResolver {
46
+ connection;
47
+ configService;
48
+ constructor(connection, configService) {
49
+ this.connection = connection;
50
+ this.configService = configService;
51
+ }
52
+ async resolve(ctx, csv) {
53
+ const idsByLookup = new Map();
54
+ const fields = new Map();
55
+ // The importer reads the same lines and headers, so both agree on which columns hold lookups
56
+ // and on row numbers.
57
+ const lines = (0, import_csv_lines_1.importCsvLines)(csv);
58
+ const headers = (0, import_csv_lines_1.importCsvHeaders)(lines);
59
+ const lookupHeaders = headers.filter((header) => this.splitHeader(header).length === 4);
60
+ if (lookupHeaders.length === 0) {
61
+ return { errors: [], rowsChecked: true, idsByLookup, fields };
62
+ }
63
+ const headerErrors = [
64
+ ...lookupHeaders.flatMap((header) => this.checkHeader(header)),
65
+ ...duplicateFieldErrors(lookupHeaders.map((header) => this.splitHeader(header))),
66
+ ];
67
+ if (headerErrors.length > 0) {
68
+ return { errors: headerErrors, rowsChecked: false, idsByLookup, fields };
69
+ }
70
+ const columns = lookupHeaders.map((header) => {
71
+ const [owner, fieldName, , column] = this.splitHeader(header);
72
+ const config = this.findFieldConfig(header);
73
+ const lookup = { entityName: config.entity.name, column };
74
+ fields.set(`${owner}:${fieldName}`, lookup);
75
+ return {
76
+ index: headers.indexOf(header),
77
+ list: config.list === true,
78
+ lookup,
79
+ entity: config.entity,
80
+ };
81
+ });
82
+ let rows;
83
+ try {
84
+ rows = this.readValues(lines, columns);
85
+ }
86
+ catch (error) {
87
+ return {
88
+ errors: [`Could not read the file: ${error.message}`],
89
+ rowsChecked: false,
90
+ idsByLookup,
91
+ fields,
92
+ };
93
+ }
94
+ const matches = await this.findLiveRows(ctx, rows);
95
+ const errors = [];
96
+ for (const { line, values } of rows) {
97
+ for (const { column, value } of values) {
98
+ const key = relationLookupKey(column.lookup, value);
99
+ const found = matches.get(key) ?? [];
100
+ if (found.length === 1) {
101
+ idsByLookup.set(key, found[0]);
102
+ }
103
+ else {
104
+ errors.push(`Row ${line}: ${lookupError(column.lookup, value, found.length)}`);
105
+ }
106
+ }
107
+ }
108
+ return { errors, rowsChecked: true, idsByLookup, fields };
109
+ }
110
+ splitHeader(header) {
111
+ if (!header.startsWith('product:') && !header.startsWith('variant:')) {
112
+ return [];
113
+ }
114
+ return header.split(':');
115
+ }
116
+ checkHeader(header) {
117
+ const [owner, fieldName, entitySegment, column] = this.splitHeader(header);
118
+ const config = this.findFieldConfig(header);
119
+ if (!config) {
120
+ const entity = owner === 'product' ? 'Product' : 'ProductVariant';
121
+ return [`Column '${header}': ${entity} has no custom field named '${fieldName}'`];
122
+ }
123
+ if (config.type !== 'relation') {
124
+ return [`Column '${header}': a lookup works only on relation custom fields`];
125
+ }
126
+ const { entity } = config;
127
+ const expectedSegment = entity.name.toLowerCase();
128
+ // `:sku` on a ProductVariant relation never read the third segment, so files written with any
129
+ // segment there keep importing.
130
+ const isLegacySku = entity === core_1.ProductVariant && column === exports.SKU_LOOKUP;
131
+ if (!isLegacySku && entitySegment.toLowerCase() !== expectedSegment) {
132
+ return [
133
+ `Column '${header}': the third segment must be '${expectedSegment}', the related entity`,
134
+ ];
135
+ }
136
+ if (!this.findLookupColumn(entity, column)) {
137
+ return [`Invalid lookup column "${column}" on ${entity.name}`];
138
+ }
139
+ return [];
140
+ }
141
+ findFieldConfig(header) {
142
+ const [owner, fieldName] = this.splitHeader(header);
143
+ const configs = owner === 'product'
144
+ ? this.configService.customFields.Product
145
+ : this.configService.customFields.ProductVariant;
146
+ return configs.find((config) => config.name === fieldName);
147
+ }
148
+ /**
149
+ * A plain text column of the entity, so a CSV cell compares to it without a cast. Relation and
150
+ * embedded columns (custom fields among them) are not lookups.
151
+ */
152
+ findLookupColumn(entity, propertyName) {
153
+ // Not findColumnWithPropertyName: it can return a custom field (embedded) column of that name.
154
+ const column = this.connection.rawConnection
155
+ .getMetadata(entity)
156
+ .columns.find((candidate) => candidate.propertyName === propertyName &&
157
+ !candidate.embeddedMetadata &&
158
+ !candidate.relationMetadata);
159
+ if (!column) {
160
+ return undefined;
161
+ }
162
+ const isText = column.type === String || TEXT_COLUMN_TYPES.has(String(column.type));
163
+ return isText ? column : undefined;
164
+ }
165
+ /**
166
+ * The lookup values each data row references, with the row's line as the importer numbers it:
167
+ * the header is line 1 and blank and comma-only lines are not counted.
168
+ */
169
+ readValues(lines, columns) {
170
+ const [, ...records] = (0, sync_1.parse)(lines.join('\n'), {
171
+ bom: true,
172
+ // As lenient as the importer's parser, so a file it imports is never rejected here.
173
+ relax_column_count: true,
174
+ relax_quotes: true,
175
+ });
176
+ return records
177
+ .map((record, index) => ({
178
+ line: index + 2,
179
+ values: columns.flatMap((column) => splitValues(record[column.index], column.list).map((value) => ({ column, value }))),
180
+ }))
181
+ .filter((row) => row.values.length > 0);
182
+ }
183
+ /** The ids of the live rows behind every value, keyed by `relationLookupKey`. */
184
+ async findLiveRows(ctx, rows) {
185
+ // One query (per chunk) for each entity and column, however many columns use it.
186
+ const valuesByLookup = new Map();
187
+ for (const { column, value } of rows.flatMap((row) => row.values)) {
188
+ const groupKey = relationLookupKey(column.lookup, '');
189
+ const group = valuesByLookup.get(groupKey) ?? { column, values: new Set() };
190
+ group.values.add(value);
191
+ valuesByLookup.set(groupKey, group);
192
+ }
193
+ const matches = new Map();
194
+ for (const { column, values } of valuesByLookup.values()) {
195
+ for (const row of await this.findLiveRowsByColumn(ctx, column, [...values])) {
196
+ const key = relationLookupKey(column.lookup, row.value);
197
+ matches.set(key, [...(matches.get(key) ?? []), row.id]);
198
+ }
199
+ }
200
+ return matches;
201
+ }
202
+ async findLiveRowsByColumn(ctx, { entity, lookup }, values) {
203
+ const softDeletes = this.connection.rawConnection.getMetadata(entity).findColumnWithPropertyName('deletedAt') !==
204
+ undefined;
205
+ const repository = this.connection.getRepository(ctx, entity);
206
+ const found = [];
207
+ for (let start = 0; start < values.length; start += LOOKUP_QUERY_CHUNK_SIZE) {
208
+ const chunk = values.slice(start, start + LOOKUP_QUERY_CHUNK_SIZE);
209
+ // The column name was checked against the entity's metadata in checkHeader.
210
+ const where = {
211
+ [lookup.column]: (0, typeorm_1.In)(chunk),
212
+ ...(softDeletes ? { deletedAt: (0, typeorm_1.IsNull)() } : {}),
213
+ };
214
+ const rows = await repository.find({
215
+ select: ['id', lookup.column],
216
+ where,
217
+ });
218
+ found.push(...rows.map((row) => ({
219
+ id: row.id,
220
+ value: String(row[lookup.column]),
221
+ })));
222
+ }
223
+ return found;
224
+ }
225
+ };
226
+ exports.RelationLookupResolver = RelationLookupResolver;
227
+ exports.RelationLookupResolver = RelationLookupResolver = __decorate([
228
+ (0, common_1.Injectable)(),
229
+ __metadata("design:paramtypes", [core_1.TransactionalConnection,
230
+ core_1.ConfigService])
231
+ ], RelationLookupResolver);
232
+ /** A second lookup column for a field that an earlier column already gives. */
233
+ function duplicateFieldErrors(headers) {
234
+ const seen = new Set();
235
+ const errors = [];
236
+ for (const parts of headers) {
237
+ const field = `${parts[0]}:${parts[1]}`;
238
+ if (seen.has(field)) {
239
+ errors.push(`Column '${parts.join(':')}': ${field} is already given by another column`);
240
+ }
241
+ seen.add(field);
242
+ }
243
+ return errors;
244
+ }
245
+ function lookupError(lookup, value, matchCount) {
246
+ const isSku = lookup.entityName === core_1.ProductVariant.name && lookup.column === exports.SKU_LOOKUP;
247
+ if (matchCount === 0) {
248
+ return isSku
249
+ ? `no variant with SKU ${value}`
250
+ : `no ${lookup.entityName} with ${lookup.column} ${value}`;
251
+ }
252
+ return isSku
253
+ ? `SKU ${value} matches ${matchCount} variants`
254
+ : `${lookup.column} ${value} matches ${matchCount} ${lookup.entityName} rows`;
255
+ }
256
+ /** The values in a list relation cell, written `VALUE|VALUE`. */
257
+ function splitLookupList(value) {
258
+ return value
259
+ .split('|')
260
+ .map((item) => item.trim())
261
+ .filter((item) => item !== '');
262
+ }
263
+ /** The SKUs in a list relation cell, written `SKU|SKU`. */
264
+ exports.splitSkuList = splitLookupList;
265
+ function splitValues(value, list) {
266
+ const trimmed = value?.trim() ?? '';
267
+ if (trimmed === '') {
268
+ return [];
269
+ }
270
+ return list ? splitLookupList(trimmed) : [trimmed];
271
+ }
272
+ //# sourceMappingURL=relation-lookup-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relation-lookup-resolver.js","sourceRoot":"","sources":["../../../../../packages/product-import-export-plugin/src/services/relation-lookup-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import { InjectableStrategy, RequestContext } from '@vendure/core';
2
+ /**
3
+ * @description
4
+ * One product of an import file as the {@link ImportRowValidationStrategy} sees it: the product's
5
+ * first row and every variant row under it, with the line each came from (the header is line 1,
6
+ * blank and comma-only lines are not counted).
7
+ *
8
+ * Custom fields are keyed as the Admin API input takes them: a relation field under its input name
9
+ * (`<name>Id`, or `<name>Ids` for a list) holding id strings, whether the file gave ids or a lookup
10
+ * column (`:sku`, `:code`) that has been resolved. A value that could not be resolved is left out
11
+ * (the lookup error is reported anyway), and so is an empty cell, which leaves the relation as it
12
+ * is. An Asset relation keeps its raw cell under the field name, since assets are only created when
13
+ * the row is written. Other custom fields keep their cell text under the field name.
14
+ * `product.customFields` holds the first translation's custom fields, which include every
15
+ * non-localised field.
16
+ *
17
+ * The groups are copies: changing them changes nothing that is imported.
18
+ *
19
+ * @category Import
20
+ */
21
+ export interface ImportRowGroup {
22
+ line: number;
23
+ product: {
24
+ customFields: Record<string, unknown>;
25
+ translations: unknown[];
26
+ [key: string]: unknown;
27
+ };
28
+ variants: Array<{
29
+ line: number;
30
+ variant: {
31
+ sku: string;
32
+ customFields: Record<string, unknown>;
33
+ [key: string]: unknown;
34
+ };
35
+ }>;
36
+ }
37
+ /**
38
+ * @description
39
+ * A problem with one row. Shown to the uploader as `Row <line>: <message>`.
40
+ *
41
+ * @category Import
42
+ */
43
+ export interface ImportRowError {
44
+ line: number;
45
+ message: string;
46
+ }
47
+ /**
48
+ * @description
49
+ * Checks the rows of an import file before anything is written. It runs twice: when the file is
50
+ * uploaded, before it is stored and queued (the upload answers `400` with the errors), and again
51
+ * when the import job starts, since data can change while the job waits (the job fails with the
52
+ * errors and imports nothing). Errors from relation lookups and from this strategy come back in
53
+ * one list.
54
+ *
55
+ * Set it with `ProductImportExportPlugin.init({ importOptions: { rowValidationStrategy } })`.
56
+ * `init(injector)` and `destroy()` are called when the application starts and stops.
57
+ *
58
+ * @category Import
59
+ */
60
+ export interface ImportRowValidationStrategy extends InjectableStrategy {
61
+ validate(ctx: RequestContext, rows: ImportRowGroup[]): Promise<ImportRowError[]>;
62
+ }
63
+ /**
64
+ * @description
65
+ * The default {@link ImportRowValidationStrategy}: accepts every row.
66
+ *
67
+ * @category Import
68
+ */
69
+ export declare class DefaultImportRowValidationStrategy implements ImportRowValidationStrategy {
70
+ validate(): Promise<ImportRowError[]>;
71
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultImportRowValidationStrategy = void 0;
4
+ /**
5
+ * @description
6
+ * The default {@link ImportRowValidationStrategy}: accepts every row.
7
+ *
8
+ * @category Import
9
+ */
10
+ class DefaultImportRowValidationStrategy {
11
+ async validate() {
12
+ return [];
13
+ }
14
+ }
15
+ exports.DefaultImportRowValidationStrategy = DefaultImportRowValidationStrategy;
16
+ //# sourceMappingURL=import-row-validation-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-row-validation-strategy.js","sourceRoot":"","sources":["../../../../../packages/product-import-export-plugin/src/strategies/import-row-validation-strategy.ts"],"names":[],"mappings":";;;AA8DA;;;;;GAKG;AACH,MAAa,kCAAkC;IAC7C,KAAK,CAAC,QAAQ;QACZ,OAAO,EAAE,CAAA;IACX,CAAC;CACF;AAJD,gFAIC"}
package/src/types.d.ts CHANGED
@@ -3,6 +3,7 @@ import { ParsedProduct, ParsedProductWithVariants } from './providers/import-pro
3
3
  import type { Injector, Product, ProductVariant, RequestContext } from '@vendure/core';
4
4
  import { ExportStorageStrategy } from './services/export-storage/export-storage-strategy';
5
5
  import { ImportJobStorageStrategy } from './services/import-storage/import-job-storage-strategy';
6
+ import { ImportRowValidationStrategy } from './strategies/import-row-validation-strategy';
6
7
  /**
7
8
  * @description
8
9
  * The plugin can be configured using the following options:
@@ -50,6 +51,13 @@ export interface PluginInitOptions {
50
51
  storageStrategy?: ImportOptions['storageStrategy'];
51
52
  storageStrategyFactory?: ImportOptions['storageStrategyFactory'];
52
53
  importJobStorage?: ImportOptions['importJobStorage'];
54
+ /**
55
+ * @description
56
+ * Checks the rows of an import file before anything is written: at upload, before the file is
57
+ * stored, and again when the import job starts. Defaults to
58
+ * {@link DefaultImportRowValidationStrategy}, which accepts every row.
59
+ */
60
+ rowValidationStrategy?: ImportRowValidationStrategy;
53
61
  };
54
62
  exportOptions: {
55
63
  defaultFileName?: string;
@@ -1,33 +0,0 @@
1
- import { ConfigService, ID, RequestContext, TransactionalConnection } from '@vendure/core';
2
- /** The fourth header segment that makes a relation column hold SKUs instead of ids. */
3
- export declare const SKU_LOOKUP = "sku";
4
- export interface SkuRelationResult {
5
- /** One line per problem, ready to show to the person who uploaded the file. */
6
- errors: string[];
7
- /** The id of the single live variant behind each SKU the file references. */
8
- variantIdsBySku: Map<string, ID>;
9
- /** `<owner>:<field>` keys of the relation columns whose values are SKUs. */
10
- fields: Set<string>;
11
- }
12
- /**
13
- * Resolves relation custom field columns written as `<owner>:<field>:<type>:sku`, where each
14
- * value names the related ProductVariant by SKU. Every SKU must match exactly one variant that is
15
- * not deleted, in any channel: base variants usually live outside the channel being imported into.
16
- */
17
- export declare class SkuRelationResolver {
18
- private connection;
19
- private configService;
20
- constructor(connection: TransactionalConnection, configService: ConfigService);
21
- resolve(ctx: RequestContext, csv: string): Promise<SkuRelationResult>;
22
- private splitHeader;
23
- private checkHeader;
24
- private findFieldConfig;
25
- /**
26
- * The SKUs each data row references, with the row's line as the importer numbers it: the
27
- * header is line 1 and blank and comma-only lines are not counted.
28
- */
29
- private readSkus;
30
- private findLiveVariants;
31
- }
32
- /** The SKUs in a list relation cell, written `SKU|SKU`. */
33
- export declare function splitSkuList(value: string): string[];
@@ -1,177 +0,0 @@
1
- "use strict";
2
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
- return c > 3 && r && Object.defineProperty(target, key, r), r;
7
- };
8
- var __metadata = (this && this.__metadata) || function (k, v) {
9
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.SkuRelationResolver = exports.SKU_LOOKUP = void 0;
13
- exports.splitSkuList = splitSkuList;
14
- const common_1 = require("@nestjs/common");
15
- const core_1 = require("@vendure/core");
16
- const sync_1 = require("csv-parse/sync");
17
- const typeorm_1 = require("typeorm");
18
- const import_csv_lines_1 = require("../helpers/import-csv-lines");
19
- /** The fourth header segment that makes a relation column hold SKUs instead of ids. */
20
- exports.SKU_LOOKUP = 'sku';
21
- /** SKUs per database query, well below every driver's bind parameter limit. */
22
- const SKU_QUERY_CHUNK_SIZE = 1000;
23
- /**
24
- * Resolves relation custom field columns written as `<owner>:<field>:<type>:sku`, where each
25
- * value names the related ProductVariant by SKU. Every SKU must match exactly one variant that is
26
- * not deleted, in any channel: base variants usually live outside the channel being imported into.
27
- */
28
- let SkuRelationResolver = class SkuRelationResolver {
29
- connection;
30
- configService;
31
- constructor(connection, configService) {
32
- this.connection = connection;
33
- this.configService = configService;
34
- }
35
- async resolve(ctx, csv) {
36
- const variantIdsBySku = new Map();
37
- const fields = new Set();
38
- // The importer reads the same lines and headers, so both agree on which columns hold SKUs
39
- // and on row numbers.
40
- const lines = (0, import_csv_lines_1.importCsvLines)(csv);
41
- const headers = (0, import_csv_lines_1.importCsvHeaders)(lines);
42
- const lookupHeaders = headers.filter((header) => this.splitHeader(header).length === 4);
43
- if (lookupHeaders.length === 0) {
44
- return { errors: [], variantIdsBySku, fields };
45
- }
46
- const headerErrors = lookupHeaders.flatMap((header) => this.checkHeader(header));
47
- if (headerErrors.length > 0) {
48
- return { errors: headerErrors, variantIdsBySku, fields };
49
- }
50
- const columns = lookupHeaders.map((header) => {
51
- const [owner, fieldName] = this.splitHeader(header);
52
- fields.add(`${owner}:${fieldName}`);
53
- return { index: headers.indexOf(header), list: this.findFieldConfig(header)?.list === true };
54
- });
55
- let rows;
56
- try {
57
- rows = this.readSkus(lines, columns);
58
- }
59
- catch (error) {
60
- return {
61
- errors: [`Could not read the file: ${error.message}`],
62
- variantIdsBySku,
63
- fields,
64
- };
65
- }
66
- const matches = await this.findLiveVariants(ctx, uniqueSkus(rows));
67
- const errors = [];
68
- for (const { line, skus } of rows) {
69
- for (const sku of skus) {
70
- const found = matches.get(sku) ?? [];
71
- if (found.length === 0) {
72
- errors.push(`Row ${line}: no variant with SKU ${sku}`);
73
- }
74
- else if (found.length > 1) {
75
- errors.push(`Row ${line}: SKU ${sku} matches ${found.length} variants`);
76
- }
77
- else {
78
- variantIdsBySku.set(sku, found[0]);
79
- }
80
- }
81
- }
82
- return { errors, variantIdsBySku, fields };
83
- }
84
- splitHeader(header) {
85
- if (!header.startsWith('product:') && !header.startsWith('variant:')) {
86
- return [];
87
- }
88
- return header.split(':');
89
- }
90
- checkHeader(header) {
91
- const lookup = this.splitHeader(header)[3];
92
- if (lookup !== exports.SKU_LOOKUP) {
93
- return [`Column '${header}': only ':${exports.SKU_LOOKUP}' is supported as a lookup`];
94
- }
95
- const config = this.findFieldConfig(header);
96
- if (!config) {
97
- const [owner, fieldName] = this.splitHeader(header);
98
- const entity = owner === 'product' ? 'Product' : 'ProductVariant';
99
- return [`Column '${header}': ${entity} has no custom field named '${fieldName}'`];
100
- }
101
- const isVariantRelation = config?.type === 'relation' && config.entity === core_1.ProductVariant;
102
- if (!isVariantRelation) {
103
- return [
104
- `Column '${header}': ':${exports.SKU_LOOKUP}' works only on relation custom fields to ProductVariant`,
105
- ];
106
- }
107
- return [];
108
- }
109
- findFieldConfig(header) {
110
- const [owner, fieldName] = this.splitHeader(header);
111
- const configs = owner === 'product'
112
- ? this.configService.customFields.Product
113
- : this.configService.customFields.ProductVariant;
114
- return configs.find((config) => config.name === fieldName);
115
- }
116
- /**
117
- * The SKUs each data row references, with the row's line as the importer numbers it: the
118
- * header is line 1 and blank and comma-only lines are not counted.
119
- */
120
- readSkus(lines, columns) {
121
- const [, ...records] = (0, sync_1.parse)(lines.join('\n'), {
122
- bom: true,
123
- // As lenient as the importer's parser, so a file it imports is never rejected here.
124
- relax_column_count: true,
125
- relax_quotes: true,
126
- });
127
- return records
128
- .map((record, index) => ({
129
- line: index + 2,
130
- skus: columns.flatMap(({ index: column, list }) => splitSkus(record[column], list)),
131
- }))
132
- .filter((row) => row.skus.length > 0);
133
- }
134
- async findLiveVariants(ctx, skus) {
135
- const matches = new Map();
136
- if (skus.length === 0) {
137
- return matches;
138
- }
139
- const repository = this.connection.getRepository(ctx, core_1.ProductVariant);
140
- const variants = [];
141
- for (let start = 0; start < skus.length; start += SKU_QUERY_CHUNK_SIZE) {
142
- const chunk = skus.slice(start, start + SKU_QUERY_CHUNK_SIZE);
143
- variants.push(...(await repository.find({
144
- select: ['id', 'sku'],
145
- where: { sku: (0, typeorm_1.In)(chunk), deletedAt: (0, typeorm_1.IsNull)() },
146
- })));
147
- }
148
- for (const variant of variants) {
149
- matches.set(variant.sku, [...(matches.get(variant.sku) ?? []), variant.id]);
150
- }
151
- return matches;
152
- }
153
- };
154
- exports.SkuRelationResolver = SkuRelationResolver;
155
- exports.SkuRelationResolver = SkuRelationResolver = __decorate([
156
- (0, common_1.Injectable)(),
157
- __metadata("design:paramtypes", [core_1.TransactionalConnection,
158
- core_1.ConfigService])
159
- ], SkuRelationResolver);
160
- /** The SKUs in a list relation cell, written `SKU|SKU`. */
161
- function splitSkuList(value) {
162
- return value
163
- .split('|')
164
- .map((sku) => sku.trim())
165
- .filter((sku) => sku !== '');
166
- }
167
- function splitSkus(value, list) {
168
- const trimmed = value?.trim() ?? '';
169
- if (trimmed === '') {
170
- return [];
171
- }
172
- return list ? splitSkuList(trimmed) : [trimmed];
173
- }
174
- function uniqueSkus(rows) {
175
- return [...new Set(rows.flatMap((row) => row.skus))];
176
- }
177
- //# sourceMappingURL=sku-relation-resolver.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sku-relation-resolver.js","sourceRoot":"","sources":["../../../../../packages/product-import-export-plugin/src/services/sku-relation-resolver.ts"],"names":[],"mappings":"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"}