@haus-tech/product-import-export-plugin 3.0.5 → 3.0.7
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/api/plugin.controller.d.ts +8 -0
- package/dist/api/plugin.controller.js +49 -0
- package/dist/api/product-export.controller.d.ts +14 -0
- package/dist/api/product-export.controller.js +117 -0
- package/dist/api/product-import.controller.d.ts +11 -0
- package/dist/api/product-import.controller.js +63 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +5 -0
- package/dist/helpers/generate-asset-hash.d.ts +2 -0
- package/dist/helpers/generate-asset-hash.js +15 -0
- package/dist/helpers/normalize-string.d.ts +1 -0
- package/dist/helpers/normalize-string.js +16 -0
- package/dist/helpers/remove-update-at-fields.d.ts +5 -0
- package/dist/helpers/remove-update-at-fields.js +19 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +17 -0
- package/dist/product-import-export.plugin.d.ts +13 -0
- package/dist/product-import-export.plugin.js +153 -0
- package/dist/providers/import-providers/asset-importer.d.ts +31 -0
- package/dist/providers/import-providers/asset-importer.js +159 -0
- package/dist/providers/import-providers/import-parser.d.ts +137 -0
- package/dist/providers/import-providers/import-parser.js +600 -0
- package/dist/providers/import-providers/product-importer.d.ts +47 -0
- package/dist/providers/import-providers/product-importer.js +769 -0
- package/dist/services/extended-fast-importer.service.d.ts +44 -0
- package/dist/services/extended-fast-importer.service.js +474 -0
- package/dist/services/product-export.service.d.ts +23 -0
- package/dist/services/product-export.service.js +344 -0
- package/dist/services/product-import.service.d.ts +22 -0
- package/dist/services/product-import.service.js +84 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.js +2 -0
- package/dist/ui/components/export-dialog.component.d.ts +43 -0
- package/dist/ui/components/export-dialog.component.html +78 -0
- package/dist/ui/components/export-dialog.component.js +108 -0
- package/dist/ui/components/export-dialog.component.ts +142 -0
- package/dist/ui/components/product-import.component.d.ts +57 -0
- package/dist/ui/components/product-import.component.html +123 -0
- package/dist/ui/components/product-import.component.js +413 -0
- package/dist/ui/components/product-import.component.ts +550 -0
- package/dist/ui/product-export.service.d.ts +14 -0
- package/dist/ui/product-export.service.js +107 -0
- package/dist/ui/product-export.service.ts +107 -0
- package/dist/ui/providers.d.ts +2 -0
- package/dist/ui/providers.js +49 -0
- package/dist/ui/providers.ts +57 -0
- package/dist/ui/routes.d.ts +31 -0
- package/dist/ui/routes.js +12 -0
- package/dist/ui/routes.ts +11 -0
- package/dist/ui/translations/en.json +52 -0
- package/dist/ui/translations/sv.json +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
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.ExtendedAssetImporter = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const core_1 = require("@vendure/core");
|
|
15
|
+
const generate_asset_hash_1 = require("../../helpers/generate-asset-hash");
|
|
16
|
+
/**
|
|
17
|
+
* @description
|
|
18
|
+
* This service creates new {@link Asset} entities based on string paths provided in the CSV
|
|
19
|
+
* import format. The source files are resolved by joining the value of `importExportOptions.importAssetsDir`
|
|
20
|
+
* with the asset path. This service is used internally by the {@link Importer} service.
|
|
21
|
+
*
|
|
22
|
+
* @docsCategory import-export
|
|
23
|
+
*/
|
|
24
|
+
let ExtendedAssetImporter = class ExtendedAssetImporter {
|
|
25
|
+
configService;
|
|
26
|
+
assetService;
|
|
27
|
+
connection;
|
|
28
|
+
channelService;
|
|
29
|
+
assetMap = new Map();
|
|
30
|
+
defaultChannel;
|
|
31
|
+
/** @internal */
|
|
32
|
+
constructor(configService, assetService, connection, channelService) {
|
|
33
|
+
this.configService = configService;
|
|
34
|
+
this.assetService = assetService;
|
|
35
|
+
this.connection = connection;
|
|
36
|
+
this.channelService = channelService;
|
|
37
|
+
}
|
|
38
|
+
async onModuleInit() {
|
|
39
|
+
this.defaultChannel = await this.channelService.getDefaultChannel();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @description
|
|
43
|
+
* Creates Asset entities for the given paths, using the assetMap cache to prevent the
|
|
44
|
+
* creation of duplicates.
|
|
45
|
+
*/
|
|
46
|
+
async getAssets(assetPaths, ctx) {
|
|
47
|
+
const assets = [];
|
|
48
|
+
const errors = [];
|
|
49
|
+
const { assetImportStrategy } = this.configService.importExportOptions;
|
|
50
|
+
const uniqueAssetPaths = new Set(assetPaths);
|
|
51
|
+
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
|
+
},
|
|
86
|
+
},
|
|
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;
|
|
100
|
+
}
|
|
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
|
+
},
|
|
115
|
+
},
|
|
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,
|
|
136
|
+
};
|
|
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
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
errors.push(e.message);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { assets, errors };
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
ExtendedAssetImporter = __decorate([
|
|
153
|
+
(0, common_1.Injectable)(),
|
|
154
|
+
__metadata("design:paramtypes", [core_1.ConfigService,
|
|
155
|
+
core_1.AssetService,
|
|
156
|
+
core_1.TransactionalConnection,
|
|
157
|
+
core_1.ChannelService])
|
|
158
|
+
], ExtendedAssetImporter);
|
|
159
|
+
exports.ExtendedAssetImporter = ExtendedAssetImporter;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { GlobalFlag, LanguageCode } from '@vendure/common/lib/generated-types';
|
|
3
|
+
import { ConfigService } from '@vendure/core';
|
|
4
|
+
import { Stream } from 'stream';
|
|
5
|
+
/**
|
|
6
|
+
* @description
|
|
7
|
+
* The intermediate representation of an OptionGroup after it has been parsed
|
|
8
|
+
* by the {@link ImportParser}.
|
|
9
|
+
*
|
|
10
|
+
* @docsCategory import-export
|
|
11
|
+
* @docsPage ImportParser
|
|
12
|
+
*/
|
|
13
|
+
export interface ParsedOptionGroup {
|
|
14
|
+
translations: Array<{
|
|
15
|
+
languageCode: LanguageCode;
|
|
16
|
+
name: string;
|
|
17
|
+
values: string[];
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @description
|
|
22
|
+
* The intermediate representation of a Facet after it has been parsed
|
|
23
|
+
* by the {@link ImportParser}.
|
|
24
|
+
*
|
|
25
|
+
* @docsCategory import-export
|
|
26
|
+
* @docsPage ImportParser
|
|
27
|
+
*/
|
|
28
|
+
export interface ParsedFacet {
|
|
29
|
+
translations: Array<{
|
|
30
|
+
languageCode: LanguageCode;
|
|
31
|
+
facet: string;
|
|
32
|
+
value: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export interface ParsedAsset {
|
|
36
|
+
id?: string;
|
|
37
|
+
url: string;
|
|
38
|
+
name?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* @description
|
|
42
|
+
* The intermediate representation of a ProductVariant after it has been parsed
|
|
43
|
+
* by the {@link ImportParser}.
|
|
44
|
+
*
|
|
45
|
+
* @docsCategory import-export
|
|
46
|
+
* @docsPage ImportParser
|
|
47
|
+
*/
|
|
48
|
+
export interface ParsedProductVariant {
|
|
49
|
+
sku: string;
|
|
50
|
+
price: number;
|
|
51
|
+
taxCategory: string;
|
|
52
|
+
stockOnHand: number;
|
|
53
|
+
trackInventory: GlobalFlag;
|
|
54
|
+
assetPaths: string[];
|
|
55
|
+
assetsJson?: ParsedAsset[];
|
|
56
|
+
facets: ParsedFacet[];
|
|
57
|
+
translations: Array<{
|
|
58
|
+
languageCode: LanguageCode;
|
|
59
|
+
optionValues: string[];
|
|
60
|
+
customFields: {
|
|
61
|
+
[name: string]: string;
|
|
62
|
+
};
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* @description
|
|
67
|
+
* The intermediate representation of a Product after it has been parsed
|
|
68
|
+
* by the {@link ImportParser}.
|
|
69
|
+
*
|
|
70
|
+
* @docsCategory import-export
|
|
71
|
+
* @docsPage ImportParser
|
|
72
|
+
*/
|
|
73
|
+
export interface ParsedProduct {
|
|
74
|
+
assetPaths: string[];
|
|
75
|
+
assetsJson?: ParsedAsset[];
|
|
76
|
+
optionGroups: ParsedOptionGroup[];
|
|
77
|
+
facets: ParsedFacet[];
|
|
78
|
+
translations: Array<{
|
|
79
|
+
languageCode: LanguageCode;
|
|
80
|
+
name: string;
|
|
81
|
+
slug: string;
|
|
82
|
+
description: string;
|
|
83
|
+
customFields: {
|
|
84
|
+
[name: string]: string;
|
|
85
|
+
};
|
|
86
|
+
}>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* @description
|
|
90
|
+
* The data structure into which an import CSV file is parsed by the
|
|
91
|
+
* {@link ImportParser} `parseProducts()` method.
|
|
92
|
+
*
|
|
93
|
+
* @docsCategory import-export
|
|
94
|
+
* @docsPage ImportParser
|
|
95
|
+
*/
|
|
96
|
+
export interface ParsedProductWithVariants {
|
|
97
|
+
product: ParsedProduct;
|
|
98
|
+
variants: ParsedProductVariant[];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* @description
|
|
102
|
+
* The result returned by the {@link ImportParser} `parseProducts()` method.
|
|
103
|
+
*
|
|
104
|
+
* @docsCategory import-export
|
|
105
|
+
* @docsPage ImportParser
|
|
106
|
+
*/
|
|
107
|
+
export interface ParseResult<T> {
|
|
108
|
+
results: T[];
|
|
109
|
+
errors: string[];
|
|
110
|
+
processed: number;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* @description
|
|
114
|
+
* Validates and parses CSV files into a data structure which can then be used to created new entities.
|
|
115
|
+
* This is used internally by the {@link Importer}.
|
|
116
|
+
*
|
|
117
|
+
* @docsCategory import-export
|
|
118
|
+
* @docsPage ImportParser
|
|
119
|
+
* @docsWeight 0
|
|
120
|
+
*/
|
|
121
|
+
export declare class ImportParser {
|
|
122
|
+
private configService;
|
|
123
|
+
/** @internal */
|
|
124
|
+
constructor(configService: ConfigService);
|
|
125
|
+
/**
|
|
126
|
+
* @description
|
|
127
|
+
* Parses the contents of the [product import CSV file](/guides/developer-guide/importing-data/#product-import-format) and
|
|
128
|
+
* returns a data structure which can then be used to populate Vendure using the {@link FastImporterService}.
|
|
129
|
+
*/
|
|
130
|
+
parseProducts(input: string | Stream, mainLanguage?: LanguageCode): Promise<ParseResult<ParsedProductWithVariants>>;
|
|
131
|
+
private processRawRecords;
|
|
132
|
+
private validateCustomFields;
|
|
133
|
+
private isTranslatable;
|
|
134
|
+
private validateHeaderTranslations;
|
|
135
|
+
private parseProductFromRecord;
|
|
136
|
+
private parseVariantFromRecord;
|
|
137
|
+
}
|