@haus-tech/product-import-export-plugin 3.6.12 → 3.6.14
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/CHANGELOG.md +17 -0
- package/README.md +110 -10
- package/package.json +1 -1
- package/src/constants.d.ts +1 -0
- package/src/constants.js +2 -1
- package/src/constants.js.map +1 -1
- package/src/dashboard/export-configuration-panel.tsx +1 -0
- package/src/dashboard/utils.ts +1 -0
- package/src/helpers/find-text-column.d.ts +8 -0
- package/src/helpers/find-text-column.js +32 -0
- package/src/helpers/find-text-column.js.map +1 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/index.js.map +1 -1
- package/src/product-import-export.plugin.d.ts +11 -1
- package/src/product-import-export.plugin.js +35 -3
- package/src/product-import-export.plugin.js.map +1 -1
- package/src/providers/import-providers/import-parser.d.ts +9 -0
- package/src/providers/import-providers/import-parser.js +26 -8
- package/src/providers/import-providers/import-parser.js.map +1 -1
- package/src/providers/import-providers/product-importer.d.ts +58 -10
- package/src/providers/import-providers/product-importer.js +163 -94
- package/src/providers/import-providers/product-importer.js.map +1 -1
- package/src/services/channel-column-resolver.d.ts +24 -0
- package/src/services/channel-column-resolver.js +79 -0
- package/src/services/channel-column-resolver.js.map +1 -0
- package/src/services/export-relation-lookups.d.ts +26 -0
- package/src/services/export-relation-lookups.js +56 -0
- package/src/services/export-relation-lookups.js.map +1 -0
- package/src/services/extended-fast-importer.service.d.ts +16 -0
- package/src/services/extended-fast-importer.service.js +46 -21
- package/src/services/extended-fast-importer.service.js.map +1 -1
- package/src/services/import-row-groups.d.ts +17 -0
- package/src/services/import-row-groups.js +69 -0
- package/src/services/import-row-groups.js.map +1 -0
- package/src/services/product-export.service.d.ts +9 -3
- package/src/services/product-export.service.js +57 -10
- package/src/services/product-export.service.js.map +1 -1
- package/src/services/product-import.service.d.ts +1 -3
- package/src/services/product-import.service.js +10 -9
- package/src/services/product-import.service.js.map +1 -1
- package/src/services/relation-lookup-resolver.d.ts +52 -0
- package/src/services/relation-lookup-resolver.js +245 -0
- package/src/services/relation-lookup-resolver.js.map +1 -0
- package/src/services/self-relation-custom-fields.d.ts +7 -0
- package/src/services/self-relation-custom-fields.js +24 -0
- package/src/services/self-relation-custom-fields.js.map +1 -1
- package/src/strategies/import-row-validation-strategy.d.ts +71 -0
- package/src/strategies/import-row-validation-strategy.js +16 -0
- package/src/strategies/import-row-validation-strategy.js.map +1 -0
- package/src/types.d.ts +21 -1
- package/src/ui/components/export-dialog.component.ts +2 -0
- package/src/ui/components/product-import.component.ts +1 -0
- package/src/services/sku-relation-resolver.d.ts +0 -33
- package/src/services/sku-relation-resolver.js +0 -177
- package/src/services/sku-relation-resolver.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## 3.6.14
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- **product-import-export-plugin:** export relation custom fields by a lookup column ([#62](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/pull/62))
|
|
6
|
+
- **product-import-export-plugin:** assign imported products to channels with a channels column ([#64](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/pull/64), [#63](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/issues/63))
|
|
7
|
+
|
|
8
|
+
### 🩹 Fixes
|
|
9
|
+
|
|
10
|
+
- **product-import-export-plugin:** import the asset sources the export writes ([#65](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/pull/65))
|
|
11
|
+
|
|
12
|
+
## 3.6.13
|
|
13
|
+
|
|
14
|
+
### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- **product-import-export-plugin:** link relations by any column and validate import rows ([#59](https://github.com/WeAreHausTech/haus-tech-vendure-plugins-public/pull/59))
|
|
17
|
+
|
|
1
18
|
## 3.6.12
|
|
2
19
|
|
|
3
20
|
### 🩹 Fixes
|
package/README.md
CHANGED
|
@@ -72,6 +72,7 @@ export const config = {
|
|
|
72
72
|
| `storageStrategy` | `ImportJobStorageStrategy` | local disk | Strategy instance for queued import file storage and retrieval. |
|
|
73
73
|
| `storageStrategyFactory` | `(injector: Injector) => ImportJobStorageStrategy` | `undefined` | Factory for DI-aware strategy creation (recommended for S3/remote storage). |
|
|
74
74
|
| `importJobStorage` | `'local' \| 's3'` | `'local'` | Deprecated compatibility flag. Prefer `storageStrategy` / `storageStrategyFactory`. |
|
|
75
|
+
| `rowValidationStrategy` | `ImportRowValidationStrategy` | accepts all | Checks the rows of a file before anything is written. See [Row validation](#row-validation). |
|
|
75
76
|
|
|
76
77
|
Available import storage strategies:
|
|
77
78
|
|
|
@@ -88,6 +89,7 @@ Available import storage strategies:
|
|
|
88
89
|
| `defaultExportFields` | `string[]` | See above | Fields that will be pre-selected in the export UI |
|
|
89
90
|
| `requiredExportFields` | `string[]` | `['name', 'sku']` | Fields that must always be included |
|
|
90
91
|
| `customExportColumns` | `CustomExportColumn[]`| `undefined` | Extra CSV columns resolved per variant row. See [Custom export columns](#custom-export-columns) below. |
|
|
92
|
+
| `relationLookups` | `Record<string, string>` | `undefined` | Relation custom fields written by a column of the related row instead of its id. See [Exporting relations by a column](#exporting-relations-by-a-column) below. |
|
|
91
93
|
| `storageStrategy` | `'s3' \| 'disk'` | `'disk'` | Where exported files are stored |
|
|
92
94
|
| `s3Options` | `object` | `undefined` | S3 configuration (required when `storageStrategy` is `'s3'`) |
|
|
93
95
|
|
|
@@ -98,6 +100,60 @@ The plugin supports configurable storage for exported files:
|
|
|
98
100
|
- **S3** – Stores export files in an S3-compatible bucket
|
|
99
101
|
- **Disk (default)** – Saves files locally on the server
|
|
100
102
|
|
|
103
|
+
### Row validation
|
|
104
|
+
|
|
105
|
+
`importOptions.rowValidationStrategy` lets you reject rows by your own rules. It runs on the parsed file twice:
|
|
106
|
+
|
|
107
|
+
1. At upload, before the file is stored. If it returns errors, the upload answers `400` with `{ "errors": [...] }` and nothing is queued.
|
|
108
|
+
2. When the import job starts, before anything is written, since data can change while the job waits. If it returns errors, the job fails with the same list and nothing is imported.
|
|
109
|
+
|
|
110
|
+
Each error is shown as `Row <line>: <message>`, in one list with the [relation lookup](#relations-by-sku-or-another-column) errors, ordered by row. Line 1 is the header; blank and comma-only lines are not counted. Numbers count CSV records, so a quoted cell that spans several lines is still one row.
|
|
111
|
+
|
|
112
|
+
A job that fails this way ends in the `FAILED` state with the list as its error, instead of `COMPLETED` with the errors in its result. The same holds when a relation lookup no longer matches when the job runs.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import {
|
|
116
|
+
ImportRowError,
|
|
117
|
+
ImportRowGroup,
|
|
118
|
+
ImportRowValidationStrategy,
|
|
119
|
+
} from '@haus-tech/product-import-export-plugin'
|
|
120
|
+
import { Injector, RequestContext, TransactionalConnection } from '@vendure/core'
|
|
121
|
+
|
|
122
|
+
class NoLabelOnSampleStrategy implements ImportRowValidationStrategy {
|
|
123
|
+
private connection!: TransactionalConnection
|
|
124
|
+
|
|
125
|
+
init(injector: Injector) {
|
|
126
|
+
this.connection = injector.get(TransactionalConnection)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async validate(ctx: RequestContext, rows: ImportRowGroup[]): Promise<ImportRowError[]> {
|
|
130
|
+
return rows
|
|
131
|
+
.filter((row) => row.product.customFields.labelId !== undefined)
|
|
132
|
+
.filter((row) => row.variants.some(({ variant }) => variant.sku.startsWith('SAMPLE-')))
|
|
133
|
+
.map((row) => ({ line: row.line, message: 'a sample product cannot have a label' }))
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
ProductImportExportPlugin.init({
|
|
138
|
+
importOptions: { rowValidationStrategy: new NoLabelOnSampleStrategy() },
|
|
139
|
+
exportOptions: {},
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Each `ImportRowGroup` is one product: its first row (`line`, `product`) and every variant row under it (`variants[].line`, `variants[].variant`).
|
|
144
|
+
|
|
145
|
+
- Relation custom fields are keyed by their Admin API input name (`labelId`, `labelsIds`) and hold id strings, also when the file names the related rows by a lookup column such as `:sku` or `:code`. An empty cell, and a value a lookup could not resolve, is left out.
|
|
146
|
+
- An `Asset` relation keeps its cell text under the field name, since assets are created only when the row is written. Other custom fields keep their cell text under the field name.
|
|
147
|
+
- `product.productId` is set when the product's first row has a product id in the first column, that is when the row updates an existing product by id. Without it, the importer matches existing products by variant SKU.
|
|
148
|
+
- `product.customFields` holds the custom fields of the product's first translation, which include every non-localised field; each entry of `product.translations` has its own.
|
|
149
|
+
- The groups are copies: changing them does not change what is imported.
|
|
150
|
+
|
|
151
|
+
`init(injector)` and `destroy()` are called when the application starts and stops. The default strategy accepts every row.
|
|
152
|
+
|
|
153
|
+
If `validate` throws, the upload answers `500` and nothing is queued, and a running job fails with the thrown error. Return errors instead to show them by row.
|
|
154
|
+
|
|
155
|
+
Lookup columns must be plain text columns of the related entity, so a translated field (for example a collection's `name`, which lives on its translation entity) cannot be one. The strategy does not run when a lookup header is wrong, since no row is read then.
|
|
156
|
+
|
|
101
157
|
### Custom export columns
|
|
102
158
|
|
|
103
159
|
Add columns to the exported CSV whose value is computed per variant row, for example a permalink built from the product slug and variant SKU.
|
|
@@ -143,7 +199,8 @@ export const config = {
|
|
|
143
199
|
| Column | Format | Example |
|
|
144
200
|
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
145
201
|
| `name`, `slug`, `description` | Text, or `name:en`, `name:sv` for translations | `My Product` |
|
|
146
|
-
| `assets` | Pipe-separated URLs, or JSON `[{"url":"...","name":"..."}]`
|
|
202
|
+
| `assets` | Pipe-separated URLs or existing asset sources, or JSON `[{"id":1,"url":"...","name":"..."}]` | `https://example.com/1.jpg\|source/b6/2.jpg` |
|
|
203
|
+
| `channels` | Channel tokens, pipe-separated (see [Assigning products to channels](#assigning-products-to-channels)) | `club-a\|club-b` |
|
|
147
204
|
| `facets` | `facet:value` pairs, pipe-separated | `Brand:Acme\|Category:Electronics` |
|
|
148
205
|
| `optionGroups` | Pipe-separated group names. Use `name:code` to share a group across products (see [Sharing option groups](#sharing-option-groups)) | `Size\|Color` or `size:shoe-size` |
|
|
149
206
|
| `optionValues` | Pipe-separated values (order matches option groups) | `Large\|Blue` |
|
|
@@ -162,21 +219,63 @@ export const config = {
|
|
|
162
219
|
- If a column exists in the header but the cell is empty, that field is explicitly cleared.
|
|
163
220
|
- For products with more than one variant, `optionGroups` and `optionValues` are required.
|
|
164
221
|
- The header is checked before the upload is queued. A custom field column the shop does not have, a missing required column or mixed translated and untranslated columns make the upload answer `400` with `{ "errors": ["Invalid custom field: product:clubName"] }`, the Dashboard lists the errors, and nothing is imported.
|
|
165
|
-
- Product data (`slug`, `description`, `assets`, `facets`, `optionGroups` and `product:` custom fields) is read from a product's first row, the row with a `name`. If a later variant row of the product fills one of these columns, that product is not imported and the import reports the column and line. Other products in the file still import. `id` may repeat on every row.
|
|
222
|
+
- Product data (`slug`, `description`, `assets`, `channels`, `facets`, `optionGroups` and `product:` custom fields) is read from a product's first row, the row with a `name`. If a later variant row of the product fills one of these columns, that product is not imported and the import reports the column and line. Other products in the file still import. `id` may repeat on every row.
|
|
223
|
+
- An asset value that is not a URL names an existing asset the way the export writes it: by the JSON `id`, otherwise by its `source`. It is linked without being uploaded again, so an unchanged export imports with every image kept, also with **replace**. A value that matches no asset is reported as an error. A URL is downloaded unless an asset with the same file name exists.
|
|
224
|
+
|
|
225
|
+
#### Assigning products to channels
|
|
226
|
+
|
|
227
|
+
A product is always assigned to the default channel and to the channel the import runs in. The `channels` column assigns it to more channels, named by token, so one file run from the default channel can fill several channels:
|
|
166
228
|
|
|
167
|
-
|
|
229
|
+
```csv
|
|
230
|
+
name,sku,price,channels
|
|
231
|
+
Club Tee,CLUB-TEE-M,299,club-a
|
|
232
|
+
Shared Cap,CAP-1,199,club-a|club-b
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
- The product and its variants are assigned, each variant with a price row in each channel's currency, and so are the option groups and facets the row writes. A shared option group (`name:code`) keeps the channels it has; Vendure reads a product's option groups whatever their channels. A variant added to the product later in the file gets the same channels.
|
|
236
|
+
- The column only adds. An empty cell or a file without the column leaves a product in the channels it is in; remove a product from a channel in the Admin UI or Dashboard.
|
|
237
|
+
- The upload is checked before it is queued, and the job checks again before its first write. Any problem answers `400` with the rows and nothing is imported:
|
|
238
|
+
- `Row 3: channels: no channel with token 'club-x'`
|
|
239
|
+
- `Row 2: channels: no right to update products in channel 'club-b'`: the uploader needs `UpdateCatalog` or `UpdateProduct` in every channel the file names.
|
|
240
|
+
- `Row 2: channels: an import run in channel 'club-a' can only name 'club-a', not 'club-b'`: outside the default channel, a file can only name the channel it runs in.
|
|
241
|
+
- Server code that calls `ProductImporter` with a context without a user is trusted, so it is not checked for rights; the token and channel rules still apply.
|
|
242
|
+
- The export writes the column (select `channels`): from the default channel the product's other channels, by channel id; from another channel only that channel's token, so a club's file never shows which other channels sell the product. An exported file imports again.
|
|
168
243
|
|
|
169
|
-
|
|
244
|
+
#### Relations by SKU or another column
|
|
245
|
+
|
|
246
|
+
A relation custom field can name the related row by one of its columns instead of by id. Add the column as a fourth segment to the header, after the type (the related entity's class name in lower case):
|
|
170
247
|
|
|
171
248
|
```csv
|
|
172
|
-
name,sku,price,variant:baseVariant:productvariant:sku
|
|
173
|
-
Club Tee,CLUB-TEE-M,299,BASE-TEE-M
|
|
249
|
+
name,sku,price,variant:baseVariant:productvariant:sku,product:label:productlabel:code
|
|
250
|
+
Club Tee,CLUB-TEE-M,299,BASE-TEE-M,ECO
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
- The column must be a text column of the related entity (not a relation or custom field). Otherwise the upload answers `400` with `{ "errors": ["Invalid lookup column \"title\" on ProductLabel"] }`.
|
|
254
|
+
- Each value must match exactly one row of the related entity, in any channel. Rows with a `deletedAt` are skipped. Matching is exact, so case and spacing count. A list field takes `VALUE|VALUE`.
|
|
255
|
+
- The upload is checked before it is queued. If a value matches no row or more than one, the upload answers `400` with the rows, for example `Row 2: no ProductLabel with code ECO` or `Row 3: code ECO matches 2 ProductLabel rows`. `:sku` on a relation to `ProductVariant` keeps its own wording: `Row 2: no variant with SKU BASE-TEE-M` and `Row 3: SKU BASE-TEE-M matches 2 variants`. The Dashboard lists the rows and nothing is imported. The import job checks again when it runs; if a value no longer matches exactly one row, the job fails with the same list and nothing is imported.
|
|
256
|
+
- The related rows must exist before the upload, so import them in an earlier file.
|
|
257
|
+
- The third segment must be the related entity's class name (`productvariant`, `productlabel`; case does not matter), and a field takes one lookup column. Otherwise the upload answers `400` and names the column. `:sku` on a relation to `ProductVariant` accepts any third segment, as it always has.
|
|
258
|
+
- A lookup works only on relation custom fields. Columns without a fourth segment still take ids.
|
|
259
|
+
|
|
260
|
+
#### Exporting relations by a column
|
|
261
|
+
|
|
262
|
+
The export writes relation custom fields as ids unless `exportOptions.relationLookups` names a column for the field. Keys are `<owner>:<field>`, values the column of the related entity:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
ProductImportExportPlugin.init({
|
|
266
|
+
importOptions: {},
|
|
267
|
+
exportOptions: {
|
|
268
|
+
relationLookups: {
|
|
269
|
+
'variant:baseVariant': 'sku',
|
|
270
|
+
'product:label': 'code',
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
})
|
|
174
274
|
```
|
|
175
275
|
|
|
176
|
-
-
|
|
177
|
-
- The
|
|
178
|
-
-
|
|
179
|
-
- `:sku` is the only lookup, and it works only on relation fields to `ProductVariant`. Columns without it still take ids.
|
|
276
|
+
- The export then writes the field under the lookup header (`variant:baseVariant:productvariant:sku`, `product:label:productlabel:code`) with the related rows' values, `VALUE|VALUE` for a list field. The import reads that header as described above, so the file imports again, also into another database where the ids differ.
|
|
277
|
+
- The column must be a plain text column of the related entity, and the field must be a relation custom field that does not relate to `Asset`. Otherwise the application does not start and the error names the key.
|
|
278
|
+
- A related row that is soft-deleted, or has no value in the column, is left out of the cell, since the import matches only live rows by a value. Values must be unique among live rows: a value two live rows share makes the import of the file fail with the lookup error above.
|
|
180
279
|
|
|
181
280
|
#### Sharing option groups
|
|
182
281
|
|
|
@@ -248,6 +347,7 @@ The Admin UI offers basic import and export. Some features such as the export-al
|
|
|
248
347
|
1. In the Admin UI or Dashboard, select products to export (or use bulk export).
|
|
249
348
|
2. Configure export fields (assets, facets, custom fields, etc.).
|
|
250
349
|
- For products with more than one variant, `optionGroups` and `optionValues` must be included.
|
|
350
|
+
- `channels` is available as a selectable export field (see [Assigning products to channels](#assigning-products-to-channels)).
|
|
251
351
|
- `id` (the Vendure product id) is available as a selectable export field. It is written after the translated `name`/`slug`/`description` columns, so it is never CSV column 0, since import treats column 0 positionally as a product id for by-id matching.
|
|
252
352
|
- If `customExportColumns` is configured, only the columns whose `name` is selected are written to the CSV.
|
|
253
353
|
- The job reports progress per page; the Dashboard job queue shows it.
|
package/package.json
CHANGED
package/src/constants.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export declare const PRODUCT_IMPORT_EXPORT_PLUGIN_OPTIONS: unique symbol;
|
|
|
2
2
|
export declare const EXPORT_STORAGE_STRATEGY: unique symbol;
|
|
3
3
|
export declare const IMPORT_JOB_STORAGE_STRATEGY: unique symbol;
|
|
4
4
|
export declare const loggerCtx = "ProductImportExportPlugin";
|
|
5
|
+
export declare const IMPORT_ROW_VALIDATION_STRATEGY: unique symbol;
|
package/src/constants.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loggerCtx = exports.IMPORT_JOB_STORAGE_STRATEGY = exports.EXPORT_STORAGE_STRATEGY = exports.PRODUCT_IMPORT_EXPORT_PLUGIN_OPTIONS = void 0;
|
|
3
|
+
exports.IMPORT_ROW_VALIDATION_STRATEGY = exports.loggerCtx = exports.IMPORT_JOB_STORAGE_STRATEGY = exports.EXPORT_STORAGE_STRATEGY = exports.PRODUCT_IMPORT_EXPORT_PLUGIN_OPTIONS = void 0;
|
|
4
4
|
exports.PRODUCT_IMPORT_EXPORT_PLUGIN_OPTIONS = Symbol('PRODUCT_IMPORT_EXPORT_PLUGIN_OPTIONS');
|
|
5
5
|
exports.EXPORT_STORAGE_STRATEGY = Symbol('EXPORT_STORAGE_STRATEGY');
|
|
6
6
|
exports.IMPORT_JOB_STORAGE_STRATEGY = Symbol('IMPORT_JOB_STORAGE_STRATEGY');
|
|
7
7
|
exports.loggerCtx = 'ProductImportExportPlugin';
|
|
8
|
+
exports.IMPORT_ROW_VALIDATION_STRATEGY = Symbol('IMPORT_ROW_VALIDATION_STRATEGY');
|
|
8
9
|
//# sourceMappingURL=constants.js.map
|
package/src/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../packages/product-import-export-plugin/src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,oCAAoC,GAAG,MAAM,CAAC,sCAAsC,CAAC,CAAA;AACrF,QAAA,uBAAuB,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAA;AAC3D,QAAA,2BAA2B,GAAG,MAAM,CAAC,6BAA6B,CAAC,CAAA;AACnE,QAAA,SAAS,GAAG,2BAA2B,CAAA"}
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../packages/product-import-export-plugin/src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,oCAAoC,GAAG,MAAM,CAAC,sCAAsC,CAAC,CAAA;AACrF,QAAA,uBAAuB,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAA;AAC3D,QAAA,2BAA2B,GAAG,MAAM,CAAC,6BAA6B,CAAC,CAAA;AACnE,QAAA,SAAS,GAAG,2BAA2B,CAAA;AACvC,QAAA,8BAA8B,GAAG,MAAM,CAAC,gCAAgC,CAAC,CAAA"}
|
package/src/dashboard/utils.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Type, VendureEntity } from '@vendure/core';
|
|
2
|
+
import { DataSource } from 'typeorm';
|
|
3
|
+
import { ColumnMetadata } from 'typeorm/metadata/ColumnMetadata';
|
|
4
|
+
/**
|
|
5
|
+
* A plain text column of the entity, so a CSV cell compares to it without a cast. Relation and
|
|
6
|
+
* embedded columns (custom fields among them) are not lookups.
|
|
7
|
+
*/
|
|
8
|
+
export declare function findTextColumn(dataSource: DataSource, entity: Type<VendureEntity>, propertyName: string): ColumnMetadata | undefined;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findTextColumn = findTextColumn;
|
|
4
|
+
/** Column types a CSV cell can be matched against without a cast. */
|
|
5
|
+
const TEXT_COLUMN_TYPES = new Set([
|
|
6
|
+
'varchar',
|
|
7
|
+
'character varying',
|
|
8
|
+
'nvarchar',
|
|
9
|
+
'text',
|
|
10
|
+
'char',
|
|
11
|
+
'character',
|
|
12
|
+
'nchar',
|
|
13
|
+
'citext',
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* A plain text column of the entity, so a CSV cell compares to it without a cast. Relation and
|
|
17
|
+
* embedded columns (custom fields among them) are not lookups.
|
|
18
|
+
*/
|
|
19
|
+
function findTextColumn(dataSource, entity, propertyName) {
|
|
20
|
+
// Not findColumnWithPropertyName: it can return a custom field (embedded) column of that name.
|
|
21
|
+
const column = dataSource
|
|
22
|
+
.getMetadata(entity)
|
|
23
|
+
.columns.find((candidate) => candidate.propertyName === propertyName &&
|
|
24
|
+
!candidate.embeddedMetadata &&
|
|
25
|
+
!candidate.relationMetadata);
|
|
26
|
+
if (!column) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const isText = column.type === String || TEXT_COLUMN_TYPES.has(String(column.type));
|
|
30
|
+
return isText ? column : undefined;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=find-text-column.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-text-column.js","sourceRoot":"","sources":["../../../../../packages/product-import-export-plugin/src/helpers/find-text-column.ts"],"names":[],"mappings":";;AAoBA,wCAmBC;AAnCD,qEAAqE;AACrE,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC;IAChC,SAAS;IACT,mBAAmB;IACnB,UAAU;IACV,MAAM;IACN,MAAM;IACN,WAAW;IACX,OAAO;IACP,QAAQ;CACT,CAAC,CAAA;AAEF;;;GAGG;AACH,SAAgB,cAAc,CAC5B,UAAsB,EACtB,MAA2B,EAC3B,YAAoB;IAEpB,+FAA+F;IAC/F,MAAM,MAAM,GAAG,UAAU;SACtB,WAAW,CAAC,MAAM,CAAC;SACnB,OAAO,CAAC,IAAI,CACX,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,YAAY,KAAK,YAAY;QACvC,CAAC,SAAS,CAAC,gBAAgB;QAC3B,CAAC,SAAS,CAAC,gBAAgB,CAC9B,CAAA;IACH,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IACnF,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;AACpC,CAAC"}
|
package/src/index.d.ts
CHANGED
|
@@ -6,4 +6,5 @@ export * from './services/export-storage/s3-export-storage-strategy';
|
|
|
6
6
|
export * from './services/import-storage/import-job-storage-strategy';
|
|
7
7
|
export * from './services/import-storage/local-import-job-storage-strategy';
|
|
8
8
|
export * from './services/import-storage/s3-import-job-storage-strategy';
|
|
9
|
+
export * from './strategies/import-row-validation-strategy';
|
|
9
10
|
export type { S3ExportStorageConfig } from './services/export-storage.util';
|
package/src/index.js
CHANGED
|
@@ -21,4 +21,5 @@ __exportStar(require("./services/export-storage/s3-export-storage-strategy"), ex
|
|
|
21
21
|
__exportStar(require("./services/import-storage/import-job-storage-strategy"), exports);
|
|
22
22
|
__exportStar(require("./services/import-storage/local-import-job-storage-strategy"), exports);
|
|
23
23
|
__exportStar(require("./services/import-storage/s3-import-job-storage-strategy"), exports);
|
|
24
|
+
__exportStar(require("./strategies/import-row-validation-strategy"), exports);
|
|
24
25
|
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/product-import-export-plugin/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iEAA8C;AAG9C,oFAAiE;AACjE,0FAAuE;AACvE,uFAAoE;AACpE,wFAAqE;AACrE,8FAA2E;AAC3E,2FAAwE"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/product-import-export-plugin/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iEAA8C;AAG9C,oFAAiE;AACjE,0FAAuE;AACvE,uFAAoE;AACpE,wFAAqE;AACrE,8FAA2E;AAC3E,2FAAwE;AACxE,8EAA2D"}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
+
import { OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common';
|
|
1
2
|
import { Type } from '@vendure/core';
|
|
2
3
|
import { AdminUiExtension } from '@vendure/ui-devkit/compiler';
|
|
4
|
+
import { ModuleRef } from '@nestjs/core';
|
|
3
5
|
import { PluginInitOptions } from './types';
|
|
4
|
-
|
|
6
|
+
import { ImportRowValidationStrategy } from './strategies/import-row-validation-strategy';
|
|
7
|
+
export declare class ProductImportExportPlugin implements OnApplicationBootstrap, OnApplicationShutdown {
|
|
8
|
+
private moduleRef;
|
|
9
|
+
private rowValidationStrategy;
|
|
5
10
|
static options: PluginInitOptions;
|
|
11
|
+
constructor(moduleRef: ModuleRef, rowValidationStrategy: ImportRowValidationStrategy);
|
|
12
|
+
/** @internal */
|
|
13
|
+
onApplicationBootstrap(): Promise<void>;
|
|
14
|
+
/** @internal */
|
|
15
|
+
onApplicationShutdown(): Promise<void>;
|
|
6
16
|
/**
|
|
7
17
|
* @description
|
|
8
18
|
* Initialise the plugin with the given import/export options, applying sensible
|
|
@@ -38,9 +38,16 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
38
38
|
return result;
|
|
39
39
|
};
|
|
40
40
|
})();
|
|
41
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
+
};
|
|
41
47
|
var ProductImportExportPlugin_1;
|
|
42
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
49
|
exports.ProductImportExportPlugin = void 0;
|
|
50
|
+
const common_1 = require("@nestjs/common");
|
|
44
51
|
const core_1 = require("@vendure/core");
|
|
45
52
|
const core_2 = require("@nestjs/core");
|
|
46
53
|
const path = __importStar(require("path"));
|
|
@@ -59,11 +66,13 @@ const product_import_service_1 = require("./services/product-import.service");
|
|
|
59
66
|
const extended_fast_importer_service_1 = require("./services/extended-fast-importer.service");
|
|
60
67
|
const product_export_service_1 = require("./services/product-export.service");
|
|
61
68
|
const product_export_queue_service_1 = require("./services/product-export-queue.service");
|
|
62
|
-
const
|
|
69
|
+
const relation_lookup_resolver_1 = require("./services/relation-lookup-resolver");
|
|
70
|
+
const channel_column_resolver_1 = require("./services/channel-column-resolver");
|
|
63
71
|
const local_export_storage_strategy_1 = require("./services/export-storage/local-export-storage-strategy");
|
|
64
72
|
const local_import_job_storage_strategy_1 = require("./services/import-storage/local-import-job-storage-strategy");
|
|
73
|
+
const import_row_validation_strategy_1 = require("./strategies/import-row-validation-strategy");
|
|
65
74
|
const RESERVED_EXPORT_COLUMN_NAMES = new Set([
|
|
66
|
-
'id', 'name', 'slug', 'description', 'assets', 'facets', 'optionGroups', 'optionValues',
|
|
75
|
+
'id', 'name', 'slug', 'description', 'assets', 'channels', 'facets', 'optionGroups', 'optionValues',
|
|
67
76
|
'sku', 'price', 'taxCategory', 'stockOnHand', 'trackInventory', 'variantAssets',
|
|
68
77
|
'variantFacets', 'enabled',
|
|
69
78
|
]);
|
|
@@ -87,7 +96,21 @@ function validateCustomExportColumns(columns) {
|
|
|
87
96
|
}
|
|
88
97
|
let ProductImportExportPlugin = class ProductImportExportPlugin {
|
|
89
98
|
static { ProductImportExportPlugin_1 = this; }
|
|
99
|
+
moduleRef;
|
|
100
|
+
rowValidationStrategy;
|
|
90
101
|
static options;
|
|
102
|
+
constructor(moduleRef, rowValidationStrategy) {
|
|
103
|
+
this.moduleRef = moduleRef;
|
|
104
|
+
this.rowValidationStrategy = rowValidationStrategy;
|
|
105
|
+
}
|
|
106
|
+
/** @internal */
|
|
107
|
+
async onApplicationBootstrap() {
|
|
108
|
+
await this.rowValidationStrategy.init?.(new core_1.Injector(this.moduleRef));
|
|
109
|
+
}
|
|
110
|
+
/** @internal */
|
|
111
|
+
async onApplicationShutdown() {
|
|
112
|
+
await this.rowValidationStrategy.destroy?.();
|
|
113
|
+
}
|
|
91
114
|
/**
|
|
92
115
|
* @description
|
|
93
116
|
* Initialise the plugin with the given import/export options, applying sensible
|
|
@@ -216,6 +239,11 @@ exports.ProductImportExportPlugin = ProductImportExportPlugin = ProductImportExp
|
|
|
216
239
|
},
|
|
217
240
|
inject: [core_2.ModuleRef],
|
|
218
241
|
},
|
|
242
|
+
{
|
|
243
|
+
provide: constants_1.IMPORT_ROW_VALIDATION_STRATEGY,
|
|
244
|
+
useFactory: () => ProductImportExportPlugin.options?.importOptions?.rowValidationStrategy ??
|
|
245
|
+
new import_row_validation_strategy_1.DefaultImportRowValidationStrategy(),
|
|
246
|
+
},
|
|
219
247
|
product_import_service_1.ProductImportService,
|
|
220
248
|
product_importer_1.ProductImporter,
|
|
221
249
|
extended_fast_importer_service_1.ExtendedFastImporterService,
|
|
@@ -223,7 +251,8 @@ exports.ProductImportExportPlugin = ProductImportExportPlugin = ProductImportExp
|
|
|
223
251
|
asset_importer_1.ExtendedAssetImporter,
|
|
224
252
|
product_export_service_1.ProductExportService,
|
|
225
253
|
product_export_queue_service_1.ProductExportQueueService,
|
|
226
|
-
|
|
254
|
+
relation_lookup_resolver_1.RelationLookupResolver,
|
|
255
|
+
channel_column_resolver_1.ChannelColumnResolver,
|
|
227
256
|
],
|
|
228
257
|
controllers: [
|
|
229
258
|
product_import_controller_1.ProductImportController,
|
|
@@ -267,5 +296,8 @@ exports.ProductImportExportPlugin = ProductImportExportPlugin = ProductImportExp
|
|
|
267
296
|
*
|
|
268
297
|
* @category Plugin
|
|
269
298
|
*/
|
|
299
|
+
,
|
|
300
|
+
__param(1, (0, common_1.Inject)(constants_1.IMPORT_ROW_VALIDATION_STRATEGY)),
|
|
301
|
+
__metadata("design:paramtypes", [core_2.ModuleRef, Object])
|
|
270
302
|
], ProductImportExportPlugin);
|
|
271
303
|
//# sourceMappingURL=product-import-export.plugin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-import-export.plugin.js","sourceRoot":"","sources":["../../../../packages/product-import-export-plugin/src/product-import-export.plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"product-import-export.plugin.js","sourceRoot":"","sources":["../../../../packages/product-import-export-plugin/src/product-import-export.plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -46,6 +46,8 @@ export interface ParsedAsset {
|
|
|
46
46
|
* @docsPage ImportParser
|
|
47
47
|
*/
|
|
48
48
|
export interface ParsedProductVariant {
|
|
49
|
+
/** The file line of the variant's row (the header is line 1, blank lines are not counted). */
|
|
50
|
+
line?: number;
|
|
49
51
|
sku: string;
|
|
50
52
|
price: number | undefined;
|
|
51
53
|
taxCategory?: string;
|
|
@@ -73,11 +75,18 @@ export interface ParsedProductVariant {
|
|
|
73
75
|
* @docsPage ImportParser
|
|
74
76
|
*/
|
|
75
77
|
export interface ParsedProduct {
|
|
78
|
+
/** The file line of the product's first row (the header is line 1, blank lines are not counted). */
|
|
79
|
+
line?: number;
|
|
76
80
|
assetPaths: string[];
|
|
77
81
|
assetsJson?: ParsedAsset[];
|
|
78
82
|
optionGroups: ParsedOptionGroup[];
|
|
79
83
|
optionGroupsProvided: boolean;
|
|
80
84
|
facets: ParsedFacet[];
|
|
85
|
+
/**
|
|
86
|
+
* The tokens in the `channels` column: channels the product is assigned to on top of the
|
|
87
|
+
* default channel and the channel the import runs in. Empty when the column is missing or empty.
|
|
88
|
+
*/
|
|
89
|
+
channelTokens: string[];
|
|
81
90
|
translations: Array<{
|
|
82
91
|
languageCode: LanguageCode;
|
|
83
92
|
name: string;
|
|
@@ -71,14 +71,18 @@ let ImportParser = class ImportParser {
|
|
|
71
71
|
return new Promise((resolve, reject) => {
|
|
72
72
|
let errors = [];
|
|
73
73
|
if (typeof input === 'string') {
|
|
74
|
-
(0, csv_parse_1.parse)(input, options,
|
|
74
|
+
(0, csv_parse_1.parse)(input, options, (err, records) => {
|
|
75
75
|
if (err) {
|
|
76
76
|
errors = errors.concat(err);
|
|
77
77
|
}
|
|
78
78
|
if (records) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
// A throw here must reject, or the caller (an upload request) waits forever.
|
|
80
|
+
this.processRawRecords(records, mainLanguage)
|
|
81
|
+
.then((parseResult) => {
|
|
82
|
+
errors = errors.concat(parseResult.errors);
|
|
83
|
+
resolve({ results: parseResult.results, errors, processed: parseResult.processed });
|
|
84
|
+
})
|
|
85
|
+
.catch(reject);
|
|
82
86
|
}
|
|
83
87
|
else {
|
|
84
88
|
resolve({ results: [], errors, processed: 0 });
|
|
@@ -159,8 +163,10 @@ let ImportParser = class ImportParser {
|
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
165
|
currentRow = {
|
|
162
|
-
product: await this.parseProductFromRecord(r, usedLanguages, mainLanguage),
|
|
163
|
-
variants: [
|
|
166
|
+
product: { line, ...(await this.parseProductFromRecord(r, usedLanguages, mainLanguage)) },
|
|
167
|
+
variants: [
|
|
168
|
+
{ line, ...(await this.parseVariantFromRecord(r, usedLanguages, mainLanguage)) },
|
|
169
|
+
],
|
|
164
170
|
};
|
|
165
171
|
currentRowRejected = false;
|
|
166
172
|
}
|
|
@@ -172,7 +178,10 @@ let ImportParser = class ImportParser {
|
|
|
172
178
|
`enter it on the product's first row (the row with a name) on line ${line}`);
|
|
173
179
|
currentRowRejected = true;
|
|
174
180
|
}
|
|
175
|
-
currentRow.variants.push(
|
|
181
|
+
currentRow.variants.push({
|
|
182
|
+
line,
|
|
183
|
+
...(await this.parseVariantFromRecord(r, usedLanguages, mainLanguage)),
|
|
184
|
+
});
|
|
176
185
|
}
|
|
177
186
|
}
|
|
178
187
|
const optionError = validateOptionValueCount(r, currentRow, mainLanguage);
|
|
@@ -422,6 +431,7 @@ let ImportParser = class ImportParser {
|
|
|
422
431
|
optionGroups,
|
|
423
432
|
optionGroupsProvided,
|
|
424
433
|
facets,
|
|
434
|
+
channelTokens: (0, unique_1.unique)(parseStringArray(r.channels)),
|
|
425
435
|
translations: await Promise.all(translations),
|
|
426
436
|
};
|
|
427
437
|
return parsedProduct;
|
|
@@ -600,7 +610,15 @@ function mapRowToObject(columns, row) {
|
|
|
600
610
|
}, {});
|
|
601
611
|
}
|
|
602
612
|
/** Columns read from a product's first row only (their translated forms too, e.g. `slug:en`). */
|
|
603
|
-
const PRODUCT_COLUMNS = new Set([
|
|
613
|
+
const PRODUCT_COLUMNS = new Set([
|
|
614
|
+
'name',
|
|
615
|
+
'slug',
|
|
616
|
+
'description',
|
|
617
|
+
'assets',
|
|
618
|
+
'facets',
|
|
619
|
+
'optionGroups',
|
|
620
|
+
'channels',
|
|
621
|
+
]);
|
|
604
622
|
/**
|
|
605
623
|
* Returns the first filled product column in a variant row (a row without a name), if any.
|
|
606
624
|
* `id` is not one: the exporter repeats the product id on every row of a product.
|