@haus-tech/product-import-export-plugin 3.0.7 → 3.0.9
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,108 +54,132 @@ 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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
.of(cachedAsset.id)
|
|
61
|
-
.add(ctx?.channel);
|
|
62
|
-
cachedAsset.channels.push(ctx?.channel);
|
|
63
|
-
}
|
|
64
|
-
if (cachedAsset.name !== assetPath.name && assetPath.name) {
|
|
65
|
-
this.connection.getRepository(ctx, core_1.Asset).update(cachedAsset.id, {
|
|
66
|
-
name: assetPath.name,
|
|
67
|
-
});
|
|
68
|
-
// Update the cached asset with the new name
|
|
69
|
-
this.assetMap.set(assetPath.url, { ...cachedAsset, name: assetPath.name });
|
|
70
|
-
assets.push({ ...cachedAsset, name: assetPath.name });
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
assets.push(cachedAsset);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
try {
|
|
78
|
-
const assetHash = (0, generate_asset_hash_1.generateAssetHash)({
|
|
79
|
-
source: assetPath.url,
|
|
80
|
-
});
|
|
81
|
-
const assetFromHash = await this.connection.getRepository(ctx, core_1.Asset).findOne({
|
|
82
|
-
where: {
|
|
83
|
-
customFields: {
|
|
84
|
-
hash: assetHash,
|
|
85
|
-
},
|
|
59
|
+
try {
|
|
60
|
+
const assetHash = (0, generate_asset_hash_1.generateAssetHash)({
|
|
61
|
+
source: assetPath.url,
|
|
62
|
+
});
|
|
63
|
+
const assetFromHash = await this.connection.getRepository(ctx, core_1.Asset).findOne({
|
|
64
|
+
where: {
|
|
65
|
+
customFields: {
|
|
66
|
+
hash: assetHash,
|
|
86
67
|
},
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this.assetMap.set(assetPath.url, assetFromHash);
|
|
98
|
-
assets.push(assetFromHash);
|
|
99
|
-
continue;
|
|
68
|
+
},
|
|
69
|
+
relations: ['channels'],
|
|
70
|
+
});
|
|
71
|
+
if (assetFromHash) {
|
|
72
|
+
// Check if the asset name has changed
|
|
73
|
+
if ((assetPath.name && assetFromHash.name !== assetPath.name) ||
|
|
74
|
+
assetFromHash.channels.some((channel) => channel.id !== ctx?.channelId)) {
|
|
75
|
+
assetFromHash.name = assetPath.name || assetFromHash.name;
|
|
76
|
+
assetFromHash.channels.push(ctx?.channel);
|
|
77
|
+
await this.connection.getRepository(ctx, core_1.Asset).save(assetFromHash);
|
|
100
78
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
79
|
+
this.assetMap.set(assetPath.url, assetFromHash);
|
|
80
|
+
assets.push(assetFromHash);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const stream = await this.getStreamFromPath(assetPath.url);
|
|
84
|
+
if (stream) {
|
|
85
|
+
const createdAsset = await this.assetService.createFromFileStream(stream, assetPath.url, ctx);
|
|
86
|
+
if ((0, core_1.isGraphQlErrorResult)(createdAsset)) {
|
|
87
|
+
errors.push(createdAsset.message);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
let assetToUpdate = createdAsset;
|
|
91
|
+
let shouldDeleteCreatedAsset = false;
|
|
92
|
+
const assetFromHash = await this.connection.getRepository(ctx, core_1.Asset).findOne({
|
|
93
|
+
where: {
|
|
94
|
+
customFields: {
|
|
95
|
+
hash: assetHash,
|
|
115
96
|
},
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
this.assetMap.set(assetPath.url, assetToUpdate);
|
|
130
|
-
assets.push(assetToUpdate);
|
|
131
|
-
assetToUpdate.name = assetPath.name || assetToUpdate.name;
|
|
132
|
-
assetToUpdate.channels.push(ctx?.channel);
|
|
133
|
-
assetToUpdate.customFields = {
|
|
134
|
-
...assetToUpdate.customFields,
|
|
135
|
-
hash: assetHash,
|
|
97
|
+
},
|
|
98
|
+
relations: ['channels'],
|
|
99
|
+
});
|
|
100
|
+
if (assetFromHash) {
|
|
101
|
+
shouldDeleteCreatedAsset = true;
|
|
102
|
+
assetToUpdate = { ...assetFromHash, name: assetPath.name || assetFromHash.name };
|
|
103
|
+
}
|
|
104
|
+
if (assetPath.id) {
|
|
105
|
+
shouldDeleteCreatedAsset = true;
|
|
106
|
+
assetToUpdate = {
|
|
107
|
+
...assetToUpdate,
|
|
108
|
+
id: assetPath.id,
|
|
136
109
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
110
|
+
}
|
|
111
|
+
this.assetMap.set(assetPath.url, assetToUpdate);
|
|
112
|
+
assets.push(assetToUpdate);
|
|
113
|
+
assetToUpdate.name = assetPath.name || assetToUpdate.name;
|
|
114
|
+
assetToUpdate.channels.push(ctx?.channel);
|
|
115
|
+
assetToUpdate.customFields = {
|
|
116
|
+
...assetToUpdate.customFields,
|
|
117
|
+
hash: assetHash,
|
|
118
|
+
};
|
|
119
|
+
await this.connection.getRepository(ctx, core_1.Asset).save(assetToUpdate);
|
|
120
|
+
if (shouldDeleteCreatedAsset) {
|
|
121
|
+
this.connection.getRepository(ctx, core_1.Asset).delete(createdAsset.id);
|
|
141
122
|
}
|
|
142
123
|
}
|
|
143
124
|
}
|
|
144
|
-
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
if (e.message.includes('no elements in sequence')) {
|
|
128
|
+
errors.push(`Could not find asset at path "${assetPath.url}"`);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
145
131
|
errors.push(e.message);
|
|
146
132
|
}
|
|
147
133
|
}
|
|
148
134
|
}
|
|
149
135
|
return { assets, errors };
|
|
150
136
|
}
|
|
137
|
+
getStreamFromPath(assetPath) {
|
|
138
|
+
if (/^https?:\/\//.test(assetPath)) {
|
|
139
|
+
return this.getStreamFromUrl(assetPath);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
return this.getStreamFromLocalFile(assetPath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
getStreamFromUrl(assetUrl) {
|
|
146
|
+
const retryCount = 3;
|
|
147
|
+
const retryDelayMs = 200;
|
|
148
|
+
return (0, rxjs_1.lastValueFrom)((0, rxjs_1.from)(fetchUrlWithAxios(assetUrl)).pipe((0, operators_1.retryWhen)((errors) => errors.pipe((0, operators_1.tap)((error) => {
|
|
149
|
+
core_1.Logger.verbose(error.message);
|
|
150
|
+
if (error.message.includes('Status code: 404')) {
|
|
151
|
+
throw error; // Stop retrying on 404 errors
|
|
152
|
+
}
|
|
153
|
+
core_1.Logger.verbose(`CustomAssetImportStrategy: retrying fetchUrl for ${assetUrl}`);
|
|
154
|
+
}), (0, operators_1.delay)(retryDelayMs ?? 200), (0, operators_1.take)(retryCount ?? 3)))));
|
|
155
|
+
}
|
|
156
|
+
getStreamFromLocalFile(assetPath) {
|
|
157
|
+
const { importAssetsDir } = this.configService.importExportOptions;
|
|
158
|
+
const filename = path_1.default.join(importAssetsDir, assetPath);
|
|
159
|
+
if (fs_extra_1.default.existsSync(filename)) {
|
|
160
|
+
const fileStat = fs_extra_1.default.statSync(filename);
|
|
161
|
+
if (fileStat.isFile()) {
|
|
162
|
+
try {
|
|
163
|
+
const stream = fs_extra_1.default.createReadStream(filename);
|
|
164
|
+
return stream;
|
|
165
|
+
}
|
|
166
|
+
catch (err) {
|
|
167
|
+
core_1.Logger.error(`Error creating read stream for local file "${filename}": ${err.message}`);
|
|
168
|
+
throw err;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const errorMessage = `Could not find file "${filename}"`;
|
|
173
|
+
core_1.Logger.error(errorMessage);
|
|
174
|
+
throw new Error(errorMessage);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const errorMessage = `File "${filename}" does not exist`;
|
|
179
|
+
core_1.Logger.error(errorMessage);
|
|
180
|
+
throw new Error(errorMessage);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
151
183
|
};
|
|
152
184
|
ExtendedAssetImporter = __decorate([
|
|
153
185
|
(0, common_1.Injectable)(),
|
|
@@ -157,3 +189,20 @@ ExtendedAssetImporter = __decorate([
|
|
|
157
189
|
core_1.ChannelService])
|
|
158
190
|
], ExtendedAssetImporter);
|
|
159
191
|
exports.ExtendedAssetImporter = ExtendedAssetImporter;
|
|
192
|
+
async function fetchUrlWithAxios(urlString) {
|
|
193
|
+
try {
|
|
194
|
+
const response = await axios_1.default.get(urlString, {
|
|
195
|
+
responseType: 'stream',
|
|
196
|
+
timeout: 5000, // 5 seconds timeout
|
|
197
|
+
});
|
|
198
|
+
if (response.status !== 200) {
|
|
199
|
+
core_1.Logger.error(`Failed to fetch "${urlString}", statusCode: ${response.status}`);
|
|
200
|
+
throw new Error(`Request failed. Status code: ${response.status}`);
|
|
201
|
+
}
|
|
202
|
+
return response.data;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
core_1.Logger.error(`Error fetching URL "${urlString}": ${error.message}`);
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|