@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.
- package/CHANGELOG.md +12 -0
- package/README.md +65 -8
- package/package.json +1 -1
- package/src/constants.d.ts +1 -0
- package/src/constants.js +2 -1
- package/src/constants.js.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/product-import-export.plugin.d.ts +11 -1
- package/src/product-import-export.plugin.js +32 -2
- package/src/product-import-export.plugin.js.map +1 -1
- package/src/providers/import-providers/import-parser.d.ts +4 -0
- package/src/providers/import-providers/import-parser.js +16 -7
- package/src/providers/import-providers/import-parser.js.map +1 -1
- package/src/providers/import-providers/product-importer.d.ts +43 -10
- package/src/providers/import-providers/product-importer.js +109 -66
- package/src/providers/import-providers/product-importer.js.map +1 -1
- package/src/services/extended-fast-importer.service.d.ts +7 -0
- package/src/services/extended-fast-importer.service.js +40 -36
- package/src/services/extended-fast-importer.service.js.map +1 -1
- package/src/services/import-row-groups.d.ts +17 -0
- package/src/services/import-row-groups.js +69 -0
- package/src/services/import-row-groups.js.map +1 -0
- package/src/services/product-import.service.d.ts +1 -3
- package/src/services/product-import.service.js +10 -9
- package/src/services/product-import.service.js.map +1 -1
- package/src/services/relation-lookup-resolver.d.ts +57 -0
- package/src/services/relation-lookup-resolver.js +272 -0
- package/src/services/relation-lookup-resolver.js.map +1 -0
- package/src/strategies/import-row-validation-strategy.d.ts +71 -0
- package/src/strategies/import-row-validation-strategy.js +16 -0
- package/src/strategies/import-row-validation-strategy.js.map +1 -0
- package/src/types.d.ts +8 -0
- package/src/services/sku-relation-resolver.d.ts +0 -33
- package/src/services/sku-relation-resolver.js +0 -177
- 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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"}
|