@haus-tech/product-import-export-plugin 2.2.0-dev.0

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 (52) hide show
  1. package/dist/api/plugin.controller.d.ts +8 -0
  2. package/dist/api/plugin.controller.js +49 -0
  3. package/dist/api/product-export.controller.d.ts +14 -0
  4. package/dist/api/product-export.controller.js +117 -0
  5. package/dist/api/product-import.controller.d.ts +11 -0
  6. package/dist/api/product-import.controller.js +62 -0
  7. package/dist/constants.d.ts +2 -0
  8. package/dist/constants.js +5 -0
  9. package/dist/helpers/generate-asset-hash.d.ts +2 -0
  10. package/dist/helpers/generate-asset-hash.js +15 -0
  11. package/dist/helpers/normalize-string.d.ts +1 -0
  12. package/dist/helpers/normalize-string.js +16 -0
  13. package/dist/helpers/remove-update-at-fields.d.ts +5 -0
  14. package/dist/helpers/remove-update-at-fields.js +19 -0
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.js +17 -0
  17. package/dist/product-import-export.plugin.d.ts +13 -0
  18. package/dist/product-import-export.plugin.js +153 -0
  19. package/dist/providers/import-providers/asset-importer.d.ts +27 -0
  20. package/dist/providers/import-providers/asset-importer.js +127 -0
  21. package/dist/providers/import-providers/import-parser.d.ts +137 -0
  22. package/dist/providers/import-providers/import-parser.js +600 -0
  23. package/dist/providers/import-providers/product-importer.d.ts +47 -0
  24. package/dist/providers/import-providers/product-importer.js +729 -0
  25. package/dist/services/extended-fast-importer.service.d.ts +41 -0
  26. package/dist/services/extended-fast-importer.service.js +418 -0
  27. package/dist/services/product-export.service.d.ts +23 -0
  28. package/dist/services/product-export.service.js +340 -0
  29. package/dist/services/product-import.service.d.ts +21 -0
  30. package/dist/services/product-import.service.js +83 -0
  31. package/dist/types.d.ts +40 -0
  32. package/dist/types.js +2 -0
  33. package/dist/ui/components/export-dialog.component.d.ts +43 -0
  34. package/dist/ui/components/export-dialog.component.html +78 -0
  35. package/dist/ui/components/export-dialog.component.js +108 -0
  36. package/dist/ui/components/export-dialog.component.ts +142 -0
  37. package/dist/ui/components/product-import.component.d.ts +55 -0
  38. package/dist/ui/components/product-import.component.html +76 -0
  39. package/dist/ui/components/product-import.component.js +398 -0
  40. package/dist/ui/components/product-import.component.ts +528 -0
  41. package/dist/ui/product-export.service.d.ts +14 -0
  42. package/dist/ui/product-export.service.js +107 -0
  43. package/dist/ui/product-export.service.ts +107 -0
  44. package/dist/ui/providers.d.ts +2 -0
  45. package/dist/ui/providers.js +49 -0
  46. package/dist/ui/providers.ts +57 -0
  47. package/dist/ui/routes.d.ts +29 -0
  48. package/dist/ui/routes.js +12 -0
  49. package/dist/ui/routes.ts +11 -0
  50. package/dist/ui/translations/en.json +44 -0
  51. package/dist/ui/translations/sv.json +44 -0
  52. package/package.json +30 -0
@@ -0,0 +1,600 @@
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.ImportParser = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const generated_types_1 = require("@vendure/common/lib/generated-types");
15
+ const unique_1 = require("@vendure/common/lib/unique");
16
+ const core_1 = require("@vendure/core");
17
+ const csv_parse_1 = require("csv-parse");
18
+ const normalize_string_1 = require("../../helpers/normalize-string");
19
+ const lodash_1 = require("lodash");
20
+ const baseTranslatableColumns = [
21
+ 'name',
22
+ 'slug',
23
+ 'description',
24
+ 'facets',
25
+ 'optionGroups',
26
+ 'optionValues',
27
+ 'variantFacets',
28
+ ];
29
+ const requiredColumns = [
30
+ 'name',
31
+ // 'slug',
32
+ // 'description',
33
+ // 'assets',
34
+ // 'facets',
35
+ // 'optionGroups',
36
+ // 'optionValues',
37
+ 'sku',
38
+ // 'price',
39
+ // 'taxCategory',
40
+ // 'variantAssets',
41
+ // 'variantFacets',
42
+ ];
43
+ /**
44
+ * @description
45
+ * Validates and parses CSV files into a data structure which can then be used to created new entities.
46
+ * This is used internally by the {@link Importer}.
47
+ *
48
+ * @docsCategory import-export
49
+ * @docsPage ImportParser
50
+ * @docsWeight 0
51
+ */
52
+ let ImportParser = class ImportParser {
53
+ configService;
54
+ /** @internal */
55
+ constructor(configService) {
56
+ this.configService = configService;
57
+ }
58
+ /**
59
+ * @description
60
+ * Parses the contents of the [product import CSV file](/guides/developer-guide/importing-data/#product-import-format) and
61
+ * returns a data structure which can then be used to populate Vendure using the {@link FastImporterService}.
62
+ */
63
+ async parseProducts(input, mainLanguage = this.configService.defaultLanguageCode) {
64
+ const options = {
65
+ trim: true,
66
+ relax_column_count: true,
67
+ };
68
+ return new Promise((resolve, reject) => {
69
+ let errors = [];
70
+ if (typeof input === 'string') {
71
+ (0, csv_parse_1.parse)(input, options, (err, records) => {
72
+ if (err) {
73
+ errors = errors.concat(err);
74
+ }
75
+ if (records) {
76
+ const parseResult = this.processRawRecords(records, mainLanguage);
77
+ errors = errors.concat(parseResult.errors);
78
+ resolve({ results: parseResult.results, errors, processed: parseResult.processed });
79
+ }
80
+ else {
81
+ resolve({ results: [], errors, processed: 0 });
82
+ }
83
+ });
84
+ }
85
+ else {
86
+ const parser = (0, csv_parse_1.parse)(options);
87
+ const records = [];
88
+ // input.on('open', () => input.pipe(parser));
89
+ input.pipe(parser);
90
+ parser.on('readable', () => {
91
+ let record;
92
+ // eslint-disable-next-line no-cond-assign
93
+ while ((record = parser.read())) {
94
+ records.push(record);
95
+ }
96
+ });
97
+ parser.on('error', reject);
98
+ parser.on('end', () => {
99
+ const parseResult = this.processRawRecords(records, mainLanguage);
100
+ errors = errors.concat(parseResult.errors);
101
+ resolve({ results: parseResult.results, errors, processed: parseResult.processed });
102
+ });
103
+ }
104
+ });
105
+ }
106
+ processRawRecords(records, mainLanguage) {
107
+ const results = [];
108
+ const errors = [];
109
+ let currentRow;
110
+ const headerRow = records[0];
111
+ const rest = records.slice(1);
112
+ const totalProducts = rest.map((row) => row[0]).filter((name) => name.trim() !== '').length;
113
+ const customFieldErrors = this.validateCustomFields(headerRow);
114
+ if (customFieldErrors.length > 0) {
115
+ return { results: [], errors: customFieldErrors, processed: 0 };
116
+ }
117
+ const translationError = this.validateHeaderTranslations(headerRow);
118
+ if (translationError) {
119
+ return { results: [], errors: [translationError], processed: 0 };
120
+ }
121
+ const columnError = validateRequiredColumns(headerRow);
122
+ if (columnError) {
123
+ return { results: [], errors: [columnError], processed: 0 };
124
+ }
125
+ const usedLanguages = usedLanguageCodes(headerRow);
126
+ let line = 1;
127
+ for (const record of rest) {
128
+ line++;
129
+ const columnCountError = validateColumnCount(headerRow, record);
130
+ if (columnCountError) {
131
+ errors.push(columnCountError + ` on line ${line}`);
132
+ continue;
133
+ }
134
+ const r = mapRowToObject(headerRow, record);
135
+ if (getRawMainTranslation(r, 'name', mainLanguage)) {
136
+ if (currentRow) {
137
+ populateOptionGroupValues(currentRow);
138
+ results.push(currentRow);
139
+ }
140
+ currentRow = {
141
+ product: this.parseProductFromRecord(r, usedLanguages, mainLanguage),
142
+ variants: [this.parseVariantFromRecord(r, usedLanguages, mainLanguage)],
143
+ };
144
+ }
145
+ else {
146
+ if (currentRow) {
147
+ currentRow.variants.push(this.parseVariantFromRecord(r, usedLanguages, mainLanguage));
148
+ }
149
+ }
150
+ const optionError = validateOptionValueCount(r, currentRow, mainLanguage);
151
+ if (optionError) {
152
+ errors.push(optionError + ` on line ${line}`);
153
+ }
154
+ }
155
+ if (currentRow) {
156
+ populateOptionGroupValues(currentRow);
157
+ results.push(currentRow);
158
+ }
159
+ return { results, errors, processed: totalProducts };
160
+ }
161
+ validateCustomFields(rowKeys) {
162
+ const errors = [];
163
+ for (const rowKey of rowKeys) {
164
+ const baseKey = getBaseKey(rowKey);
165
+ const parts = baseKey.split(':');
166
+ if (parts.length === 1) {
167
+ continue;
168
+ }
169
+ if (parts.length === 2) {
170
+ let customFieldConfigs = [];
171
+ if (parts[0] === 'product') {
172
+ customFieldConfigs = this.configService.customFields.Product;
173
+ }
174
+ else if (parts[0] === 'variant') {
175
+ customFieldConfigs = this.configService.customFields.ProductVariant;
176
+ }
177
+ else {
178
+ continue;
179
+ }
180
+ const customFieldConfig = customFieldConfigs.find((config) => config.name === parts[1]);
181
+ if (customFieldConfig) {
182
+ continue;
183
+ }
184
+ }
185
+ errors.push(`Invalid custom field: ${rowKey}`);
186
+ }
187
+ return errors;
188
+ }
189
+ isTranslatable(baseKey) {
190
+ const parts = baseKey.split(':');
191
+ if (parts.length === 1) {
192
+ return baseTranslatableColumns.includes(baseKey);
193
+ }
194
+ if (parts.length === 2) {
195
+ let customFieldConfigs;
196
+ if (parts[0] === 'product') {
197
+ customFieldConfigs = this.configService.customFields.Product;
198
+ }
199
+ else if (parts[0] === 'variant') {
200
+ customFieldConfigs = this.configService.customFields.ProductVariant;
201
+ }
202
+ else {
203
+ throw new core_1.InternalServerError(`Invalid column header '${baseKey}'`);
204
+ }
205
+ const customFieldConfig = customFieldConfigs.find((config) => config.name === parts[1]);
206
+ if (!customFieldConfig) {
207
+ throw new core_1.InternalServerError(`Could not find custom field config for column header '${baseKey}'`);
208
+ }
209
+ return customFieldConfig.type === 'localeString';
210
+ }
211
+ throw new core_1.InternalServerError(`Invalid column header '${baseKey}'`);
212
+ }
213
+ validateHeaderTranslations(rowKeys) {
214
+ const missing = [];
215
+ const languageCodes = usedLanguageCodes(rowKeys);
216
+ const baseKeys = usedBaseKeys(rowKeys);
217
+ for (const baseKey of baseKeys) {
218
+ const translatedKeys = languageCodes.map((code) => [baseKey, code].join(':'));
219
+ if (rowKeys.includes(baseKey)) {
220
+ // Untranslated column header is used -> there should be no translated ones
221
+ if (rowKeys.some((key) => translatedKeys.includes(key))) {
222
+ return `The import file must not contain both translated and untranslated columns for field '${baseKey}'`;
223
+ }
224
+ }
225
+ else {
226
+ if (!this.isTranslatable(baseKey) && translatedKeys.some((key) => rowKeys.includes(key))) {
227
+ return `The '${baseKey}' field is not translatable.`;
228
+ }
229
+ // All column headers must exist for all translations
230
+ for (const translatedKey of translatedKeys) {
231
+ if (!rowKeys.includes(translatedKey)) {
232
+ missing.push(translatedKey);
233
+ }
234
+ }
235
+ }
236
+ }
237
+ if (missing.length) {
238
+ return `The import file is missing the following translations: ${missing
239
+ .map((m) => `"${m}"`)
240
+ .join(', ')}`;
241
+ }
242
+ }
243
+ parseProductFromRecord(r, usedLanguages, mainLanguage) {
244
+ const translationCodes = usedLanguages.length === 0 ? [mainLanguage] : usedLanguages;
245
+ const optionGroups = [];
246
+ for (const languageCode of translationCodes) {
247
+ const rawTranslOptionGroups = r.hasOwnProperty(`optionGroups:${languageCode}`)
248
+ ? r[`optionGroups:${languageCode}`]
249
+ : r.optionGroups;
250
+ if (!rawTranslOptionGroups) {
251
+ continue;
252
+ }
253
+ const translatedOptionGroups = parseStringArray(rawTranslOptionGroups);
254
+ if (optionGroups.length === 0) {
255
+ for (const translatedOptionGroup of translatedOptionGroups) {
256
+ optionGroups.push({ translations: [] });
257
+ }
258
+ }
259
+ for (const i of optionGroups.map((optionGroup, index) => index)) {
260
+ optionGroups[i].translations.push({
261
+ languageCode,
262
+ name: translatedOptionGroups[i],
263
+ values: [],
264
+ });
265
+ }
266
+ }
267
+ const facets = [];
268
+ for (const languageCode of translationCodes) {
269
+ const rawTranslatedFacets = r.hasOwnProperty(`facets:${languageCode}`)
270
+ ? r[`facets:${languageCode}`]
271
+ : r.facets;
272
+ if (!rawTranslatedFacets) {
273
+ continue;
274
+ }
275
+ const translatedFacets = parseStringArray(rawTranslatedFacets);
276
+ if (facets.length === 0) {
277
+ for (const translatedFacet of translatedFacets) {
278
+ facets.push({ translations: [] });
279
+ }
280
+ }
281
+ for (const i of facets.map((facet, index) => index)) {
282
+ const [facet, value] = translatedFacets[i].split(':');
283
+ facets[i].translations.push({
284
+ languageCode,
285
+ facet,
286
+ value,
287
+ });
288
+ }
289
+ }
290
+ const translations = translationCodes.map((languageCode) => {
291
+ const translatedFields = getRawTranslatedFields(r, languageCode);
292
+ const parsedTranslatedCustomFields = parseCustomFields('product', translatedFields);
293
+ const parsedUntranslatedCustomFields = parseCustomFields('product', getRawUntranslatedFields(r));
294
+ const parsedCustomFields = {
295
+ ...parsedUntranslatedCustomFields,
296
+ ...parsedTranslatedCustomFields,
297
+ };
298
+ const name = translatedFields.hasOwnProperty('name')
299
+ ? parseString(translatedFields.name)
300
+ : r.name;
301
+ let slug;
302
+ if (translatedFields.hasOwnProperty('slug')) {
303
+ slug = parseString(translatedFields.slug);
304
+ }
305
+ else {
306
+ slug = parseString(r.slug);
307
+ }
308
+ if (slug.length === 0) {
309
+ slug = (0, normalize_string_1.normalizeString)(name, '-', languageCode);
310
+ }
311
+ return {
312
+ languageCode,
313
+ name,
314
+ slug,
315
+ description: translatedFields.hasOwnProperty('description')
316
+ ? parseString(translatedFields.description)
317
+ : r.description || '',
318
+ customFields: parsedCustomFields,
319
+ };
320
+ });
321
+ const jsonAssets = parseAssetsIfJson(r.assets);
322
+ const parsedProduct = {
323
+ assetPaths: jsonAssets.length ? jsonAssets.map(({ url }) => url) : parseStringArray(r.assets),
324
+ assetsJson: jsonAssets,
325
+ optionGroups,
326
+ facets,
327
+ translations,
328
+ };
329
+ return parsedProduct;
330
+ }
331
+ parseVariantFromRecord(r, usedLanguages, mainLanguage) {
332
+ const translationCodes = usedLanguages.length === 0 ? [mainLanguage] : usedLanguages;
333
+ const facets = [];
334
+ for (const languageCode of translationCodes) {
335
+ const rawTranslatedFacets = r.hasOwnProperty(`variantFacets:${languageCode}`)
336
+ ? r[`variantFacets:${languageCode}`]
337
+ : r.variantFacets;
338
+ if (!rawTranslatedFacets) {
339
+ continue;
340
+ }
341
+ const translatedFacets = parseStringArray(rawTranslatedFacets);
342
+ if (facets.length === 0) {
343
+ for (const translatedFacet of translatedFacets) {
344
+ facets.push({ translations: [] });
345
+ }
346
+ }
347
+ for (const i of facets.map((facet, index) => index)) {
348
+ const [facet, value] = translatedFacets[i].split(':');
349
+ facets[i].translations.push({
350
+ languageCode,
351
+ facet,
352
+ value,
353
+ });
354
+ }
355
+ }
356
+ const translations = translationCodes.map((languageCode) => {
357
+ const rawTranslOptionValues = r.hasOwnProperty(`optionValues:${languageCode}`)
358
+ ? r[`optionValues:${languageCode}`]
359
+ : r.optionValues;
360
+ const translatedOptionValues = parseStringArray(rawTranslOptionValues);
361
+ const translatedFields = getRawTranslatedFields(r, languageCode);
362
+ const parsedTranslatedCustomFields = parseCustomFields('variant', translatedFields);
363
+ const parsedUntranslatedCustomFields = parseCustomFields('variant', getRawUntranslatedFields(r));
364
+ const parsedCustomFields = {
365
+ ...parsedUntranslatedCustomFields,
366
+ ...parsedTranslatedCustomFields,
367
+ };
368
+ return {
369
+ languageCode,
370
+ optionValues: translatedOptionValues,
371
+ customFields: parsedCustomFields,
372
+ };
373
+ });
374
+ const jsonAssets = parseAssetsIfJson(r.variantAssets);
375
+ const parsedVariant = {
376
+ sku: parseString(r.sku),
377
+ price: parseNumber(r.price),
378
+ taxCategory: parseString(r.taxCategory),
379
+ stockOnHand: parseNumber(r.stockOnHand),
380
+ trackInventory: r.trackInventory == null || r.trackInventory === ''
381
+ ? generated_types_1.GlobalFlag.INHERIT
382
+ : parseBoolean(r.trackInventory)
383
+ ? generated_types_1.GlobalFlag.TRUE
384
+ : generated_types_1.GlobalFlag.FALSE,
385
+ assetPaths: jsonAssets.length
386
+ ? jsonAssets.map(({ url }) => url)
387
+ : parseStringArray(r.variantAssets),
388
+ assetsJson: jsonAssets,
389
+ facets,
390
+ translations,
391
+ };
392
+ return parsedVariant;
393
+ }
394
+ };
395
+ ImportParser = __decorate([
396
+ (0, common_1.Injectable)(),
397
+ __metadata("design:paramtypes", [core_1.ConfigService])
398
+ ], ImportParser);
399
+ exports.ImportParser = ImportParser;
400
+ function populateOptionGroupValues(currentRow) {
401
+ for (const translation of currentRow.product.translations) {
402
+ const values = currentRow.variants.map((variant) => {
403
+ const variantTranslation = variant.translations.find((t) => t.languageCode === translation.languageCode);
404
+ if (!variantTranslation) {
405
+ throw new core_1.InternalServerError(`No translation '${translation.languageCode}' for variant SKU '${variant.sku}'`);
406
+ }
407
+ return variantTranslation.optionValues;
408
+ });
409
+ currentRow.product.optionGroups.forEach((og, i) => {
410
+ const ogTranslation = og.translations.find((t) => t.languageCode === translation.languageCode);
411
+ if (ogTranslation) {
412
+ ogTranslation.values = (0, unique_1.unique)(values.map((v) => v[i]));
413
+ }
414
+ else {
415
+ // throw new InternalServerError(
416
+ // `No translation '${translation.languageCode}' for option groups'`,
417
+ // )
418
+ }
419
+ });
420
+ }
421
+ }
422
+ function getLanguageCode(rowKey) {
423
+ const parts = rowKey.split(':');
424
+ if (parts.length === 2) {
425
+ if (parts[1] in generated_types_1.LanguageCode) {
426
+ return parts[1];
427
+ }
428
+ }
429
+ if (parts.length === 3) {
430
+ if (['product', 'productVariant'].includes(parts[0]) && parts[2] in generated_types_1.LanguageCode) {
431
+ return parts[2];
432
+ }
433
+ }
434
+ }
435
+ function getBaseKey(rowKey) {
436
+ const parts = rowKey.split(':');
437
+ if (getLanguageCode(rowKey)) {
438
+ parts.pop();
439
+ return parts.join(':');
440
+ }
441
+ else {
442
+ return rowKey;
443
+ }
444
+ }
445
+ function usedLanguageCodes(rowKeys) {
446
+ const languageCodes = [];
447
+ for (const rowKey of rowKeys) {
448
+ const languageCode = getLanguageCode(rowKey);
449
+ if (languageCode && !languageCodes.includes(languageCode)) {
450
+ languageCodes.push(languageCode);
451
+ }
452
+ }
453
+ return languageCodes;
454
+ }
455
+ function usedBaseKeys(rowKeys) {
456
+ const baseKeys = [];
457
+ for (const rowKey of rowKeys) {
458
+ const baseKey = getBaseKey(rowKey);
459
+ if (!baseKeys.includes(baseKey)) {
460
+ baseKeys.push(baseKey);
461
+ }
462
+ }
463
+ return baseKeys;
464
+ }
465
+ function validateRequiredColumns(r) {
466
+ const rowKeys = r;
467
+ const missing = [];
468
+ const languageCodes = usedLanguageCodes(rowKeys);
469
+ for (const col of requiredColumns) {
470
+ if (!rowKeys.includes(col)) {
471
+ if (languageCodes.length > 0 && rowKeys.includes(`${col}:${languageCodes[0]}`)) {
472
+ continue; // If one translation is present, they are all present (we did 'validateHeaderTranslations' before)
473
+ }
474
+ missing.push(col);
475
+ }
476
+ }
477
+ if (missing.length) {
478
+ return `The import file is missing the following columns: ${missing
479
+ .map((m) => `"${m}"`)
480
+ .join(', ')}`;
481
+ }
482
+ }
483
+ function validateColumnCount(columns, row) {
484
+ if (columns.length !== row.length) {
485
+ return `Invalid Record Length: header length is ${columns.length}, got ${row.length}`;
486
+ }
487
+ }
488
+ function mapRowToObject(columns, row) {
489
+ return row.reduce((obj, val, i) => {
490
+ return { ...obj, [columns[i]]: val };
491
+ }, {});
492
+ }
493
+ function validateOptionValueCount(r, currentRow, mainLanguage) {
494
+ if (!currentRow) {
495
+ return;
496
+ }
497
+ const optionValueKeys = Object.keys(r).filter((key) => key.startsWith('optionValues'));
498
+ for (const key of optionValueKeys) {
499
+ const optionValues = parseStringArray(r[key]);
500
+ if (currentRow.product.optionGroups.length !== optionValues.length && key === mainLanguage) {
501
+ return `The number of optionValues in column '${key}' must match the number of optionGroups`;
502
+ }
503
+ }
504
+ }
505
+ function getRawMainTranslation(r, field, mainLanguage) {
506
+ if (r.hasOwnProperty(field)) {
507
+ return r[field];
508
+ }
509
+ else {
510
+ return r[`${field}:${mainLanguage}`];
511
+ }
512
+ }
513
+ function getRawTranslatedFields(r, languageCode) {
514
+ return Object.entries(r)
515
+ .filter(([key, value]) => key.endsWith(`:${languageCode}`))
516
+ .reduce((output, [key, value]) => {
517
+ const fieldName = key.replace(`:${languageCode}`, '');
518
+ return {
519
+ ...output,
520
+ [fieldName]: value,
521
+ };
522
+ }, {});
523
+ }
524
+ function getRawUntranslatedFields(r) {
525
+ return Object.entries(r)
526
+ .filter(([key, value]) => {
527
+ return !getLanguageCode(key);
528
+ })
529
+ .reduce((output, [key, value]) => {
530
+ return {
531
+ ...output,
532
+ [key]: value,
533
+ };
534
+ }, {});
535
+ }
536
+ function isRelationObject(value) {
537
+ try {
538
+ const parsed = JSON.parse(value);
539
+ return parsed && parsed.hasOwnProperty('id');
540
+ }
541
+ catch (e) {
542
+ return false;
543
+ }
544
+ }
545
+ function parseCustomFields(prefix, r) {
546
+ return Object.entries(r)
547
+ .filter(([key, value]) => {
548
+ return key.indexOf(`${prefix}:`) === 0;
549
+ })
550
+ .reduce((output, [key, value]) => {
551
+ const fieldName = key.replace(`${prefix}:`, '');
552
+ return {
553
+ ...output,
554
+ [fieldName]: isRelationObject(value) ? JSON.parse(value) : value,
555
+ };
556
+ }, {});
557
+ }
558
+ function parseString(input) {
559
+ return (input || '').trim();
560
+ }
561
+ function parseNumber(input) {
562
+ return +(input || '').trim();
563
+ }
564
+ function parseBoolean(input) {
565
+ if (input == null) {
566
+ return false;
567
+ }
568
+ switch (input.toLowerCase()) {
569
+ case 'true':
570
+ case '1':
571
+ case 'yes':
572
+ return true;
573
+ default:
574
+ return false;
575
+ }
576
+ }
577
+ function parseStringArray(input, separator = '|') {
578
+ return (input || '')
579
+ .trim()
580
+ .split(separator)
581
+ .map((s) => s.trim())
582
+ .filter((s) => s !== '');
583
+ }
584
+ function parseAssetsIfJson(input) {
585
+ try {
586
+ if ((0, lodash_1.startsWith)(input, '{') || (0, lodash_1.startsWith)(input, '[')) {
587
+ if (typeof input === 'string') {
588
+ const parsed = JSON.parse(input.replace(/'/g, '"').replace(/\s+/g, ' ').replace(/,\s*'/g, ", '"));
589
+ return Array.isArray(parsed) ? parsed : [parsed];
590
+ }
591
+ else {
592
+ return input;
593
+ }
594
+ }
595
+ }
596
+ catch (e) {
597
+ return [];
598
+ }
599
+ return [];
600
+ }
@@ -0,0 +1,47 @@
1
+ /// <reference types="node" />
2
+ import { ConfigService, ChannelService, FacetService, FacetValueService, TaxCategoryService, ID, ImportProgress, LanguageCode, RequestContext, OnProgressFn, ParsedFacet, ProductService, TransactionalConnection, TranslatableSaver, AssetService, Asset, ProductOptionGroupService } from '@vendure/core';
3
+ import { Stream } from 'stream';
4
+ import { Observable } from 'rxjs';
5
+ import { ExtendedFastImporterService } from '../../services/extended-fast-importer.service';
6
+ import { ParsedProductWithId } from '../../types';
7
+ import { ImportParser } from './import-parser';
8
+ import { ExtendedAssetImporter } from './asset-importer';
9
+ export declare class ProductImporter {
10
+ private connection;
11
+ private configService;
12
+ private importParser;
13
+ private assetImporter;
14
+ private fastImporter;
15
+ private translatableSaver;
16
+ private productService;
17
+ private channelService;
18
+ private facetService;
19
+ private facetValueService;
20
+ private taxCategoryService;
21
+ private assetService;
22
+ private optionGroupService;
23
+ private taxCategoryMatches;
24
+ private facetMap;
25
+ private facetValueMap;
26
+ private customFieldTypes;
27
+ private allAssets;
28
+ constructor(connection: TransactionalConnection, configService: ConfigService, importParser: ImportParser, assetImporter: ExtendedAssetImporter, fastImporter: ExtendedFastImporterService, translatableSaver: TranslatableSaver, productService: ProductService, channelService: ChannelService, facetService: FacetService, facetValueService: FacetValueService, taxCategoryService: TaxCategoryService, assetService: AssetService, optionGroupService: ProductOptionGroupService);
29
+ parseAndImport(input: string | Stream, ctx: RequestContext, updateProductSlug: boolean | undefined, mainLanguage: LanguageCode): Observable<ImportProgress>;
30
+ private extractProductIdsFromCSV;
31
+ private preprocessCsv;
32
+ private streamToString;
33
+ getAllAssets(ctx: RequestContext): Promise<Asset[]>;
34
+ setMissingAssetHashes(ctx: RequestContext): Promise<void>;
35
+ private doParseAndImport;
36
+ private getRequestContext;
37
+ importProducts(ctx: RequestContext, rows: ParsedProductWithId[], onProgress: OnProgressFn): Promise<string[]>;
38
+ private getFacetValueIds;
39
+ private processCustomFieldValues;
40
+ /**
41
+ * Attempts to match a TaxCategory entity against the name supplied in the import table. If no matches
42
+ * are found, the first TaxCategory id is returned.
43
+ */
44
+ private getMatchingTaxCategoryId;
45
+ private getTranslationByCodeOrFirst;
46
+ createFacetAndValue(facet: ParsedFacet, facetCode: string, valueCode: string): Promise<ID>;
47
+ }