@haus-tech/product-import-export-plugin 3.0.6 → 3.0.8

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.
@@ -1,6 +1,8 @@
1
+ /// <reference types="node" />
1
2
  import { OnModuleInit } from '@nestjs/common';
2
3
  import { Asset, AssetService, ChannelService, ConfigService, RequestContext, TransactionalConnection } from '@vendure/core';
3
4
  import { JsonAsset } from '../../types';
5
+ import { Readable } from 'stream';
4
6
  /**
5
7
  * @description
6
8
  * This service creates new {@link Asset} entities based on string paths provided in the CSV
@@ -28,4 +30,7 @@ export declare class ExtendedAssetImporter implements OnModuleInit {
28
30
  assets: Asset[];
29
31
  errors: string[];
30
32
  }>;
33
+ getStreamFromPath(assetPath: string): Readable | Promise<Readable>;
34
+ private getStreamFromUrl;
35
+ private getStreamFromLocalFile;
31
36
  }
@@ -8,11 +8,19 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
15
  exports.ExtendedAssetImporter = void 0;
13
16
  const common_1 = require("@nestjs/common");
14
17
  const core_1 = require("@vendure/core");
15
18
  const generate_asset_hash_1 = require("../../helpers/generate-asset-hash");
19
+ const fs_extra_1 = __importDefault(require("fs-extra"));
20
+ const axios_1 = __importDefault(require("axios"));
21
+ const path_1 = __importDefault(require("path"));
22
+ const rxjs_1 = require("rxjs");
23
+ const operators_1 = require("rxjs/operators");
16
24
  /**
17
25
  * @description
18
26
  * This service creates new {@link Asset} entities based on string paths provided in the CSV
@@ -46,7 +54,6 @@ let ExtendedAssetImporter = class ExtendedAssetImporter {
46
54
  async getAssets(assetPaths, ctx) {
47
55
  const assets = [];
48
56
  const errors = [];
49
- const { assetImportStrategy } = this.configService.importExportOptions;
50
57
  const uniqueAssetPaths = new Set(assetPaths);
51
58
  for (const assetPath of uniqueAssetPaths.values()) {
52
59
  const cachedAsset = this.assetMap.get(assetPath.url);
@@ -98,7 +105,7 @@ let ExtendedAssetImporter = class ExtendedAssetImporter {
98
105
  assets.push(assetFromHash);
99
106
  continue;
100
107
  }
101
- const stream = await assetImportStrategy.getStreamFromPath(assetPath.url);
108
+ const stream = await this.getStreamFromPath(assetPath.url);
102
109
  if (stream) {
103
110
  const createdAsset = await this.assetService.createFromFileStream(stream, assetPath.url, ctx);
104
111
  if ((0, core_1.isGraphQlErrorResult)(createdAsset)) {
@@ -142,12 +149,63 @@ let ExtendedAssetImporter = class ExtendedAssetImporter {
142
149
  }
143
150
  }
144
151
  catch (e) {
145
- errors.push(e.message);
152
+ if (e.message.includes('no elements in sequence')) {
153
+ errors.push(`Could not find asset at path "${assetPath.url}"`);
154
+ }
155
+ else {
156
+ errors.push(e.message);
157
+ }
146
158
  }
147
159
  }
148
160
  }
149
161
  return { assets, errors };
150
162
  }
163
+ getStreamFromPath(assetPath) {
164
+ if (/^https?:\/\//.test(assetPath)) {
165
+ return this.getStreamFromUrl(assetPath);
166
+ }
167
+ else {
168
+ return this.getStreamFromLocalFile(assetPath);
169
+ }
170
+ }
171
+ getStreamFromUrl(assetUrl) {
172
+ const retryCount = 3;
173
+ const retryDelayMs = 200;
174
+ return (0, rxjs_1.lastValueFrom)((0, rxjs_1.from)(fetchUrlWithAxios(assetUrl)).pipe((0, operators_1.retryWhen)((errors) => errors.pipe((0, operators_1.tap)((error) => {
175
+ core_1.Logger.verbose(error.message);
176
+ if (error.message.includes('Status code: 404')) {
177
+ throw error; // Stop retrying on 404 errors
178
+ }
179
+ core_1.Logger.verbose(`CustomAssetImportStrategy: retrying fetchUrl for ${assetUrl}`);
180
+ }), (0, operators_1.delay)(retryDelayMs ?? 200), (0, operators_1.take)(retryCount ?? 3)))));
181
+ }
182
+ getStreamFromLocalFile(assetPath) {
183
+ const { importAssetsDir } = this.configService.importExportOptions;
184
+ const filename = path_1.default.join(importAssetsDir, assetPath);
185
+ if (fs_extra_1.default.existsSync(filename)) {
186
+ const fileStat = fs_extra_1.default.statSync(filename);
187
+ if (fileStat.isFile()) {
188
+ try {
189
+ const stream = fs_extra_1.default.createReadStream(filename);
190
+ return stream;
191
+ }
192
+ catch (err) {
193
+ core_1.Logger.error(`Error creating read stream for local file "${filename}": ${err.message}`);
194
+ throw err;
195
+ }
196
+ }
197
+ else {
198
+ const errorMessage = `Could not find file "${filename}"`;
199
+ core_1.Logger.error(errorMessage);
200
+ throw new Error(errorMessage);
201
+ }
202
+ }
203
+ else {
204
+ const errorMessage = `File "${filename}" does not exist`;
205
+ core_1.Logger.error(errorMessage);
206
+ throw new Error(errorMessage);
207
+ }
208
+ }
151
209
  };
152
210
  ExtendedAssetImporter = __decorate([
153
211
  (0, common_1.Injectable)(),
@@ -157,3 +215,20 @@ ExtendedAssetImporter = __decorate([
157
215
  core_1.ChannelService])
158
216
  ], ExtendedAssetImporter);
159
217
  exports.ExtendedAssetImporter = ExtendedAssetImporter;
218
+ async function fetchUrlWithAxios(urlString) {
219
+ try {
220
+ const response = await axios_1.default.get(urlString, {
221
+ responseType: 'stream',
222
+ timeout: 5000, // 5 seconds timeout
223
+ });
224
+ if (response.status !== 200) {
225
+ core_1.Logger.error(`Failed to fetch "${urlString}", statusCode: ${response.status}`);
226
+ throw new Error(`Request failed. Status code: ${response.status}`);
227
+ }
228
+ return response.data;
229
+ }
230
+ catch (error) {
231
+ core_1.Logger.error(`Error fetching URL "${urlString}": ${error.message}`);
232
+ throw error;
233
+ }
234
+ }
@@ -35,7 +35,6 @@ export declare class ProductImporter {
35
35
  private doParseAndImport;
36
36
  private getRequestContext;
37
37
  importProducts(ctx: RequestContext, rows: ParsedProductWithId[], updatingStrategy: UpdatingStrategy, onProgress: OnProgressFn): Promise<string[]>;
38
- private getFacetValueIds;
39
38
  private processCustomFieldValues;
40
39
  /**
41
40
  * Attempts to match a TaxCategory entity against the name supplied in the import table. If no matches
@@ -413,10 +413,10 @@ let ProductImporter = class ProductImporter {
413
413
  if (createVariantAssets.errors.length) {
414
414
  errors = errors.concat(createVariantAssets.errors);
415
415
  }
416
- let facetValueIds = [];
417
- if (0 < variant.facets.length) {
418
- facetValueIds = await this.getFacetValueIds(ctx, variant.facets, languageCode);
419
- }
416
+ // let facetValueIds: ID[] = []
417
+ // if (0 < variant.facets.length) {
418
+ // facetValueIds = await this.getFacetValueIds(ctx, variant.facets, languageCode)
419
+ // }
420
420
  const variantCustomFields = await this.processCustomFieldValues(variantMainTranslation.customFields, this.configService.customFields.ProductVariant, ctx);
421
421
  const optionIds = variantMainTranslation.optionValues.map((v, index) => optionsMap[`${index}_${v}`]);
422
422
  const existingVariant = await this.connection.getRepository(ctx, core_1.ProductVariant).findOne({
@@ -504,64 +504,79 @@ let ProductImporter = class ProductImporter {
504
504
  }
505
505
  return errors;
506
506
  }
507
- async getFacetValueIds(ctx, facets, languageCode) {
508
- const facetValueIds = [];
509
- for (const item of facets) {
510
- const itemMainTranslation = this.getTranslationByCodeOrFirst(item.translations, languageCode);
511
- const facetName = itemMainTranslation.facet;
512
- const valueName = itemMainTranslation.value;
513
- let facetEntity;
514
- const cachedFacet = this.facetMap.get(facetName);
515
- if (cachedFacet) {
516
- facetEntity = cachedFacet;
517
- }
518
- else {
519
- const existing = await this.facetService.findByCode(ctx, (0, normalize_string_1.normalizeString)(facetName, '-', languageCode), languageCode);
520
- if (existing) {
521
- facetEntity = existing;
522
- }
523
- else {
524
- facetEntity = await this.facetService.create(ctx, {
525
- isPrivate: false,
526
- code: (0, normalize_string_1.normalizeString)(facetName, '-', languageCode),
527
- translations: item.translations.map((translation) => {
528
- return {
529
- languageCode: translation.languageCode,
530
- name: translation.facet,
531
- };
532
- }),
533
- });
534
- }
535
- this.facetMap.set(facetName, facetEntity);
536
- }
537
- let facetValueEntity;
538
- const facetValueMapKey = `${facetName}:${valueName}`;
539
- const cachedFacetValue = this.facetValueMap.get(facetValueMapKey);
540
- if (cachedFacetValue) {
541
- facetValueEntity = cachedFacetValue;
542
- }
543
- else {
544
- const existing = facetEntity.values.find((v) => (0, normalize_string_1.normalizeString)(v.name, '-') === (0, normalize_string_1.normalizeString)(valueName, '-', languageCode));
545
- if (existing) {
546
- facetValueEntity = existing;
547
- }
548
- else {
549
- facetValueEntity = await this.facetValueService.create(ctx, facetEntity, {
550
- code: (0, normalize_string_1.normalizeString)(valueName, '-', languageCode),
551
- translations: item.translations.map((translation) => {
552
- return {
553
- languageCode: translation.languageCode,
554
- name: translation.value,
555
- };
556
- }),
557
- });
558
- }
559
- this.facetValueMap.set(facetValueMapKey, facetValueEntity);
560
- }
561
- facetValueIds.push(facetValueEntity.id);
562
- }
563
- return facetValueIds;
564
- }
507
+ // private async getFacetValueIds(
508
+ // ctx: RequestContext,
509
+ // facets: ParsedFacet[],
510
+ // languageCode: LanguageCode,
511
+ // ): Promise<ID[]> {
512
+ // const facetValueIds: ID[] = []
513
+ // for (const item of facets) {
514
+ // const itemMainTranslation = this.getTranslationByCodeOrFirst(item.translations, languageCode)
515
+ // const facetName = itemMainTranslation.facet
516
+ // const valueName = itemMainTranslation.value
517
+ // let facetEntity: Facet
518
+ // const cachedFacet = this.facetMap.get(facetName)
519
+ // if (cachedFacet?.code === 'kategorier') {
520
+ // console.log('cachedFacet', cachedFacet)
521
+ // }
522
+ // if (cachedFacet) {
523
+ // facetEntity = cachedFacet
524
+ // } else {
525
+ // const existing = await this.facetService.findByCode(
526
+ // ctx,
527
+ // normalizeString(facetName, '-', languageCode),
528
+ // languageCode,
529
+ // )
530
+ // if (existing) {
531
+ // if (existing?.code === 'kategorier') {
532
+ // console.log('existingFacet', existing)
533
+ // }
534
+ // facetEntity = existing
535
+ // } else {
536
+ // facetEntity = await this.facetService.create(ctx, {
537
+ // isPrivate: false,
538
+ // code: normalizeString(facetName, '-', languageCode),
539
+ // translations: item.translations.map((translation) => {
540
+ // return {
541
+ // languageCode: translation.languageCode,
542
+ // name: translation.facet,
543
+ // }
544
+ // }),
545
+ // })
546
+ // }
547
+ // this.facetMap.set(facetName, facetEntity)
548
+ // }
549
+ // let facetValueEntity: FacetValue
550
+ // const facetValueMapKey = `${facetName}:${valueName}`
551
+ // const cachedFacetValue = this.facetValueMap.get(facetValueMapKey)
552
+ // if (cachedFacetValue) {
553
+ // facetValueEntity = cachedFacetValue
554
+ // if (cachedFacetValue?.code === 'duschhoerna') {
555
+ // console.log('cachedFacetValue', cachedFacetValue)
556
+ // }
557
+ // } else {
558
+ // const existing = facetEntity.values.find(
559
+ // (v) => normalizeString(v.name, '-') === normalizeString(valueName, '-', languageCode),
560
+ // )
561
+ // if (existing) {
562
+ // facetValueEntity = existing
563
+ // } else {
564
+ // facetValueEntity = await this.facetValueService.create(ctx, facetEntity, {
565
+ // code: normalizeString(valueName, '-', languageCode),
566
+ // translations: item.translations.map((translation) => {
567
+ // return {
568
+ // languageCode: translation.languageCode,
569
+ // name: translation.value,
570
+ // }
571
+ // }),
572
+ // })
573
+ // }
574
+ // this.facetValueMap.set(facetValueMapKey, facetValueEntity)
575
+ // }
576
+ // facetValueIds.push(facetValueEntity.id)
577
+ // }
578
+ // return facetValueIds
579
+ // }
565
580
  async processCustomFieldValues(customFields, config, ctx) {
566
581
  function parseBoolean(input) {
567
582
  if (input == null) {
@@ -252,7 +252,7 @@ let ExtendedFastImporterService = class ExtendedFastImporterService {
252
252
  const facetValueExists = await this.connection
253
253
  .getRepository(this.importCtx, core_1.FacetValue)
254
254
  .findOne({
255
- where: { code: input.code },
255
+ where: { code: input.code, facet: { id: input.facetId } },
256
256
  relations: ['channels', 'translations'],
257
257
  });
258
258
  if (facetValueExists) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/product-import-export-plugin",
3
- "version": "3.0.6",
3
+ "version": "3.0.8",
4
4
  "description": "A Vendure plugin for importing and exporting products using a CSV file",
5
5
  "author": "Haus Tech",
6
6
  "repository": {