@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
- const cachedAsset = this.assetMap.get(assetPath.url);
53
- if (cachedAsset) {
54
- if (ctx?.channelId &&
55
- cachedAsset.channels.some((channel) => channel.id !== ctx?.channelId)) {
56
- await this.connection
57
- .getRepository(ctx, core_1.Asset)
58
- .createQueryBuilder()
59
- .relation(core_1.Asset, 'channels')
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
- relations: ['channels'],
88
- });
89
- if (assetFromHash) {
90
- // Check if the asset name has changed
91
- if ((assetPath.name && assetFromHash.name !== assetPath.name) ||
92
- assetFromHash.channels.some((channel) => channel.id !== ctx?.channelId)) {
93
- assetFromHash.name = assetPath.name || assetFromHash.name;
94
- assetFromHash.channels.push(ctx?.channel);
95
- await this.connection.getRepository(ctx, core_1.Asset).save(assetFromHash);
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
- const stream = await assetImportStrategy.getStreamFromPath(assetPath.url);
102
- if (stream) {
103
- const createdAsset = await this.assetService.createFromFileStream(stream, assetPath.url, ctx);
104
- if ((0, core_1.isGraphQlErrorResult)(createdAsset)) {
105
- errors.push(createdAsset.message);
106
- }
107
- else {
108
- let assetToUpdate = createdAsset;
109
- let shouldDeleteCreatedAsset = false;
110
- const assetFromHash = await this.connection.getRepository(ctx, core_1.Asset).findOne({
111
- where: {
112
- customFields: {
113
- hash: assetHash,
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
- relations: ['channels'],
117
- });
118
- if (assetFromHash) {
119
- shouldDeleteCreatedAsset = true;
120
- assetToUpdate = { ...assetFromHash, name: assetPath.name || assetFromHash.name };
121
- }
122
- if (assetPath.id) {
123
- shouldDeleteCreatedAsset = true;
124
- assetToUpdate = {
125
- ...assetToUpdate,
126
- id: assetPath.id,
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
- await this.connection.getRepository(ctx, core_1.Asset).save(assetToUpdate);
138
- if (shouldDeleteCreatedAsset) {
139
- this.connection.getRepository(ctx, core_1.Asset).delete(createdAsset.id);
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
- catch (e) {
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haus-tech/product-import-export-plugin",
3
- "version": "3.0.7",
3
+ "version": "3.0.9",
4
4
  "description": "A Vendure plugin for importing and exporting products using a CSV file",
5
5
  "author": "Haus Tech",
6
6
  "repository": {