@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.
- package/dist/providers/import-providers/asset-importer.d.ts +5 -0
- package/dist/providers/import-providers/asset-importer.js +78 -3
- package/dist/providers/import-providers/product-importer.d.ts +0 -1
- package/dist/providers/import-providers/product-importer.js +77 -62
- package/dist/services/extended-fast-importer.service.js +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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) {
|