Skip to content

Commit

Permalink
feat(core): Enable importing of custom field list data
Browse files Browse the repository at this point in the history
Closes #577
  • Loading branch information
michaelbromley committed Jun 28, 2021
1 parent 4343154 commit 5d85c07
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 17 deletions.
18 changes: 18 additions & 0 deletions docs/content/developer-guide/importing-product-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,24 @@ If you have [CustomFields]({{< relref "customizing-models" >}}) defined on your
For a real example, see the [products.csv file used to populate the Vendure demo data](https://github.com/vendure-ecommerce/vendure/blob/master/packages/core/mock-data/data-sources/products.csv)
{{< /alert >}}

#### Importing `relation` custom fields

To import custom fields with the type `relation`, the value in the CSV must be a stringified object with an `id` property:

```csv
... ,product:featuredReview
... ,"{ ""id"": 123 }"
```

#### Importing `list` custom fields

To import custom fields with `list` set to `true`, the data should be separated with a pipe (`|`) character:

```csv
... ,product:keywords
... ,tablet|pad|android
```

## Initial Data

As well as product data, other initialization data can be populated using the [`InitialData` object]({{< relref "initial-data" >}}). **This format is intentionally limited**; more advanced requirements (e.g. setting up ShippingMethods that use custom checkers & calculators) should be carried out via scripts which interact with the [Admin GraphQL API]({{< relref "/docs/graphql-api/admin" >}}).
Expand Down
11 changes: 11 additions & 0 deletions packages/core/e2e/__snapshots__/import.e2e-spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ Object {
},
],
"customFields": Object {
"keywords": Array [
"paper",
"stretching",
"watercolor",
],
"owner": Object {
"id": "T_1",
},
Expand Down Expand Up @@ -114,6 +119,7 @@ exports[`Import resolver imports products 2`] = `
Object {
"assets": Array [],
"customFields": Object {
"keywords": Array [],
"owner": Object {
"id": "T_1",
},
Expand Down Expand Up @@ -160,6 +166,7 @@ exports[`Import resolver imports products 3`] = `
Object {
"assets": Array [],
"customFields": Object {
"keywords": Array [],
"owner": Object {
"id": "T_1",
},
Expand Down Expand Up @@ -250,6 +257,10 @@ exports[`Import resolver imports products 4`] = `
Object {
"assets": Array [],
"customFields": Object {
"keywords": Array [
"apron",
"clothing",
],
"owner": Object {
"id": "T_1",
},
Expand Down
22 changes: 11 additions & 11 deletions packages/core/e2e/fixtures/product-import.csv
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
name , slug , description , assets , facets , optionGroups , optionValues , sku , price , taxCategory , stockOnHand , trackInventory , variantAssets , variantFacets , product:pageType , variant:weight,product:owner
Perfect Paper Stretcher , perfect-paper-stretcher , A great device for stretching paper. , "pps1.jpg|pps2.jpg" , , size , Half Imperial , PPS12 , 45.3 , standard , 0 , false , , Brand:KB|Type:Accessory , default , 100,"{""id"": 1}"
, , , , , , Quarter Imperial , PPS14 , 32.5 , standard , 0 , false , , Brand:KB|Type:Accessory , , 100,"{""id"": 1}"
, , , , , , Full Imperial , PPSF , 59.5 , standard , -10 , false , , Brand:KB|Type:Accessory , , 100,"{""id"": 1}"
Mabef M/02 Studio Easel , , Mabef description , , , , , M02 , 910.7 , standard , 100 , false , , Brand:Mabef|Type:Easel , expanded , 300,"{""id"": 1}"
Giotto Mega Pencils , , Really mega pencils , , , box size , Box of 8 , 225400 , 4.16 , standard , , false , "box-of-8.jpg" , Collection:Xmas Sale , default , 200,"{""id"": 1}"
, , , , , , Box of 12 , 225600 , 6.24 , standard , , false , "box-of-12.jpg" , Collection:Xmas Sale , , 200,"{""id"": 1}"
name ,slug ,description ,assets ,facets ,optionGroups ,optionValues ,sku ,price,taxCategory,stockOnHand,trackInventory,variantAssets ,variantFacets ,product:pageType,variant:weight,product:owner,product:keywords
Perfect Paper Stretcher,perfect-paper-stretcher,A great device for stretching paper.,"pps1.jpg|pps2.jpg", ,size ,Half Imperial ,PPS12 ,45.3 ,standard ,0 ,false , ,Brand:KB|Type:Accessory,default ,100 ,"{""id"": 1}",paper|stretching|watercolor
, , , , , ,Quarter Imperial,PPS14 ,32.5 ,standard ,0 ,false , ,Brand:KB|Type:Accessory, ,100 ,"{""id"": 1}",
, , , , , ,Full Imperial ,PPSF ,59.5 ,standard ,-10 ,false , ,Brand:KB|Type:Accessory, ,100 ,"{""id"": 1}",
Mabef M/02 Studio Easel, ,Mabef description , , , , ,M02 ,910.7,standard ,100 ,false , ,Brand:Mabef|Type:Easel ,expanded ,300 ,"{""id"": 1}",
Giotto Mega Pencils , ,Really mega pencils , , ,box size ,Box of 8 ,225400,4.16 ,standard , ,false ,"box-of-8.jpg" ,Collection:Xmas Sale ,default ,200 ,"{""id"": 1}",
, , , , , ,Box of 12 ,225600,6.24 ,standard , ,false ,"box-of-12.jpg",Collection:Xmas Sale , ,200 ,"{""id"": 1}"",

Artists Smock , , Keeps the paint off the clothes , , Material:Denim|Collection:clothes , "size|colour" , "small|beige" , 10112 , 11.99 , reduced , , false , , , default , 500,"{""id"": 1}"
, , , , , , "large|beige" , 10113 , 11.99 , reduced , , false , , , default , 500,"{""id"": 1}"
, , , , , , "small|navy" , 10114 , 11.99 , reduced , , false , , , default , 500,"{""id"": 1}"
, , , , , , "large|navy" , 10115 , 11.99 , reduced , , false , , , default , 500,"{""id"": 1}"
Artists Smock , ,Keeps the paint off the clothes , ,Material:Denim|Collection:clothes,"size|colour","small|beige" ,10112 ,11.99,reduced , ,false , , ,default ,500 ,"{""id"": 1}",apron|clothing
, , , , , ,"large|beige" ,10113 ,11.99,reduced , ,false , , ,default ,500 ,"{""id"": 1}",
, , , , , ,"small|navy" ,10114 ,11.99,reduced , ,false , , ,default ,500 ,"{""id"": 1}",
, , , , , ,"large|navy" ,10115 ,11.99,reduced , ,false , , ,default ,500 ,"{""id"": 1}",
17 changes: 16 additions & 1 deletion packages/core/e2e/import.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ describe('Import resolver', () => {
entity: User,
eager: true,
},
{
name: 'keywords',
public: true,
nullable: true,
type: 'string',
list: true,
},
],
ProductVariant: [{ type: 'int', name: 'weight' }],
},
Expand Down Expand Up @@ -69,7 +76,7 @@ describe('Import resolver', () => {
});

expect(result.importProducts.errors).toEqual([
'Invalid Record Length: header length is 17, got 1 on line 8',
'Invalid Record Length: header length is 18, got 1 on line 8',
]);
expect(result.importProducts.imported).toBe(4);
expect(result.importProducts.processed).toBe(4);
Expand Down Expand Up @@ -114,6 +121,7 @@ describe('Import resolver', () => {
owner {
id
}
keywords
}
variants {
id
Expand Down Expand Up @@ -216,9 +224,16 @@ describe('Import resolver', () => {
expect(smock.variants[2].options.map(byCode).sort()).toEqual(['navy', 'small']);
expect(smock.variants[3].options.map(byCode).sort()).toEqual(['large', 'navy']);

// Import relation custom fields
expect(paperStretcher.customFields.owner.id).toBe('T_1');
expect(easel.customFields.owner.id).toBe('T_1');
expect(pencils.customFields.owner.id).toBe('T_1');
expect(smock.customFields.owner.id).toBe('T_1');

// Import list custom fields
expect(paperStretcher.customFields.keywords).toEqual(['paper', 'stretching', 'watercolor']);
expect(easel.customFields.keywords).toEqual([]);
expect(pencils.customFields.keywords).toEqual([]);
expect(smock.customFields.keywords).toEqual(['apron', 'clothing']);
}, 20000);
});
21 changes: 19 additions & 2 deletions packages/core/src/data-import/providers/importer/importer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Stream } from 'stream';

import { RequestContext } from '../../../api/common/request-context';
import { ConfigService } from '../../../config/config.service';
import { CustomFieldConfig } from '../../../config/custom-field/custom-field-types';
import { FacetValue } from '../../../entity/facet-value/facet-value.entity';
import { Facet } from '../../../entity/facet/facet.entity';
import { TaxCategory } from '../../../entity/tax-category/tax-category.entity';
Expand Down Expand Up @@ -160,7 +161,10 @@ export class Importer {
slug: product.slug,
},
],
customFields: product.customFields,
customFields: this.processCustomFieldValues(
product.customFields,
this.configService.customFields.Product,
),
});

const optionsMap: { [optionName: string]: ID } = {};
Expand Down Expand Up @@ -219,7 +223,10 @@ export class Importer {
},
],
price: Math.round(variant.price * 100),
customFields: variant.customFields,
customFields: this.processCustomFieldValues(
variant.customFields,
this.configService.customFields.ProductVariant,
),
});
}
imported++;
Expand Down Expand Up @@ -295,6 +302,16 @@ export class Importer {
return facetValueIds;
}

private processCustomFieldValues(customFields: { [field: string]: string }, config: CustomFieldConfig[]) {
const processed: { [field: string]: string | string[] } = {};
for (const fieldDef of config) {
const value = customFields[fieldDef.name];
processed[fieldDef.name] =
fieldDef.list === true ? value.split('|').filter(val => val.trim() !== '') : value;
}
return processed;
}

/**
* Attempts to match a TaxCategory entity against the name supplied in the import table. If no matches
* are found, the first TaxCategory id is returned.
Expand Down
4 changes: 1 addition & 3 deletions packages/core/src/service/services/customer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,9 +381,7 @@ export class CustomerService {
throw new InternalServerError('error.cannot-locate-customer-for-user');
}
if (ctx.channelId) {
await this.channelService.assignToChannels(ctx, Customer, customer.id, [
ctx.channelId,
]);
await this.channelService.assignToChannels(ctx, Customer, customer.id, [ctx.channelId]);
}
await this.historyService.createHistoryEntryForCustomer({
customerId: customer.id,
Expand Down

0 comments on commit 5d85c07

Please sign in to comment.