Browse Source

feat(core): Enable importing of custom field list data

Closes #577
Michael Bromley 4 years ago
parent
commit
5d85c07424

+ 18 - 0
docs/content/developer-guide/importing-product-data.md

@@ -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" >}}).

+ 11 - 0
packages/core/e2e/__snapshots__/import.e2e-spec.ts.snap

@@ -17,6 +17,11 @@ Object {
     },
   ],
   "customFields": Object {
+    "keywords": Array [
+      "paper",
+      "stretching",
+      "watercolor",
+    ],
     "owner": Object {
       "id": "T_1",
     },
@@ -114,6 +119,7 @@ exports[`Import resolver imports products 2`] = `
 Object {
   "assets": Array [],
   "customFields": Object {
+    "keywords": Array [],
     "owner": Object {
       "id": "T_1",
     },
@@ -160,6 +166,7 @@ exports[`Import resolver imports products 3`] = `
 Object {
   "assets": Array [],
   "customFields": Object {
+    "keywords": Array [],
     "owner": Object {
       "id": "T_1",
     },
@@ -250,6 +257,10 @@ exports[`Import resolver imports products 4`] = `
 Object {
   "assets": Array [],
   "customFields": Object {
+    "keywords": Array [
+      "apron",
+      "clothing",
+    ],
     "owner": Object {
       "id": "T_1",
     },

+ 11 - 11
packages/core/e2e/fixtures/product-import.csv

@@ -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}",

+ 16 - 1
packages/core/e2e/import.e2e-spec.ts

@@ -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' }],
         },
@@ -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);
@@ -114,6 +121,7 @@ describe('Import resolver', () => {
                                 owner {
                                     id
                                 }
+                                keywords
                             }
                             variants {
                                 id
@@ -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);
 });

+ 19 - 2
packages/core/src/data-import/providers/importer/importer.ts

@@ -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';
@@ -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 } = {};
@@ -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++;
@@ -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.

+ 1 - 3
packages/core/src/service/services/customer.service.ts

@@ -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,