Browse Source

feat(core): FacetValue Collection filter can specify logical operator

Relates to #112
Michael Bromley 6 years ago
parent
commit
f1361176d4

+ 10 - 0
packages/core/e2e/__snapshots__/collection.e2e-spec.ts.snap

@@ -41,6 +41,11 @@ Object {
           "type": "FACET_VALUE_IDS",
           "type": "FACET_VALUE_IDS",
           "value": "[\\"T_1\\"]",
           "value": "[\\"T_1\\"]",
         },
         },
+        Object {
+          "name": "containsAny",
+          "type": "BOOLEAN",
+          "value": "false",
+        },
       ],
       ],
       "code": "facet-value-filter",
       "code": "facet-value-filter",
       "description": "Filter by FacetValues",
       "description": "Filter by FacetValues",
@@ -97,6 +102,11 @@ Object {
           "type": "FACET_VALUE_IDS",
           "type": "FACET_VALUE_IDS",
           "value": "[\\"T_3\\"]",
           "value": "[\\"T_3\\"]",
         },
         },
+        Object {
+          "name": "containsAny",
+          "type": "BOOLEAN",
+          "value": "false",
+        },
       ],
       ],
       "code": "facet-value-filter",
       "code": "facet-value-filter",
       "description": "Filter by FacetValues",
       "description": "Filter by FacetValues",

+ 84 - 24
packages/core/e2e/collection.e2e-spec.ts

@@ -4,10 +4,7 @@ import gql from 'graphql-tag';
 import path from 'path';
 import path from 'path';
 
 
 import { StringOperator } from '../src/common/configurable-operation';
 import { StringOperator } from '../src/common/configurable-operation';
-import {
-    facetValueCollectionFilter,
-    variantNameCollectionFilter,
-} from '../src/config/collection/default-collection-filters';
+import { facetValueCollectionFilter, variantNameCollectionFilter } from '../src/config/collection/default-collection-filters';
 
 
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { TEST_SETUP_TIMEOUT_MS } from './config/test-config';
 import { COLLECTION_FRAGMENT, FACET_VALUE_FRAGMENT } from './graphql/fragments';
 import { COLLECTION_FRAGMENT, FACET_VALUE_FRAGMENT } from './graphql/fragments';
@@ -107,6 +104,11 @@ describe('Collection resolver', () => {
                                         value: `["${getFacetValueId('electronics')}"]`,
                                         value: `["${getFacetValueId('electronics')}"]`,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                     },
                                     },
+                                    {
+                                        name: 'containsAny',
+                                        value: `false`,
+                                        type: ConfigArgType.BOOLEAN,
+                                    },
                                 ],
                                 ],
                             },
                             },
                         ],
                         ],
@@ -138,6 +140,11 @@ describe('Collection resolver', () => {
                                         value: `["${getFacetValueId('computers')}"]`,
                                         value: `["${getFacetValueId('computers')}"]`,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                     },
                                     },
+                                    {
+                                        name: 'containsAny',
+                                        value: `false`,
+                                        type: ConfigArgType.BOOLEAN,
+                                    },
                                 ],
                                 ],
                             },
                             },
                         ],
                         ],
@@ -164,6 +171,11 @@ describe('Collection resolver', () => {
                                         value: `["${getFacetValueId('pear')}"]`,
                                         value: `["${getFacetValueId('pear')}"]`,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                     },
                                     },
+                                    {
+                                        name: 'containsAny',
+                                        value: `false`,
+                                        type: ConfigArgType.BOOLEAN,
+                                    },
                                 ],
                                 ],
                             },
                             },
                         ],
                         ],
@@ -246,7 +258,7 @@ describe('Collection resolver', () => {
         const { updateCollection } = await client.query<
         const { updateCollection } = await client.query<
             UpdateCollection.Mutation,
             UpdateCollection.Mutation,
             UpdateCollection.Variables
             UpdateCollection.Variables
-        >(UPDATE_COLLECTION, {
+            >(UPDATE_COLLECTION, {
             input: {
             input: {
                 id: pearCollection.id,
                 id: pearCollection.id,
                 assetIds: [assets[1].id],
                 assetIds: [assets[1].id],
@@ -384,7 +396,7 @@ describe('Collection resolver', () => {
             const result = await client.query<
             const result = await client.query<
                 CreateCollectionSelectVariants.Mutation,
                 CreateCollectionSelectVariants.Mutation,
                 CreateCollectionSelectVariants.Variables
                 CreateCollectionSelectVariants.Variables
-            >(CREATE_COLLECTION_SELECT_VARIANTS, {
+                >(CREATE_COLLECTION_SELECT_VARIANTS, {
                 input: {
                 input: {
                     translations: [{ languageCode: LanguageCode.en, name: 'Empty', description: '' }],
                     translations: [{ languageCode: LanguageCode.en, name: 'Empty', description: '' }],
                     filters: [],
                     filters: [],
@@ -398,7 +410,7 @@ describe('Collection resolver', () => {
                 const result = await client.query<
                 const result = await client.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
                     GetCollectionProducts.Variables
-                >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                    >(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: electronicsCollection.id,
                     id: electronicsCollection.id,
                 });
                 });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
@@ -430,7 +442,7 @@ describe('Collection resolver', () => {
                 const result = await client.query<
                 const result = await client.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
                     GetCollectionProducts.Variables
-                >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                    >(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: computersCollection.id,
                     id: computersCollection.id,
                 });
                 });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
@@ -454,14 +466,14 @@ describe('Collection resolver', () => {
                 ]);
                 ]);
             });
             });
 
 
-            it('photo and pear', async () => {
+            it('photo AND pear', async () => {
                 const result = await client.query<
                 const result = await client.query<
                     CreateCollectionSelectVariants.Mutation,
                     CreateCollectionSelectVariants.Mutation,
                     CreateCollectionSelectVariants.Variables
                     CreateCollectionSelectVariants.Variables
-                >(CREATE_COLLECTION_SELECT_VARIANTS, {
+                    >(CREATE_COLLECTION_SELECT_VARIANTS, {
                     input: {
                     input: {
                         translations: [
                         translations: [
-                            { languageCode: LanguageCode.en, name: 'Photo Pear', description: '' },
+                            { languageCode: LanguageCode.en, name: 'Photo AND Pear', description: '' },
                         ],
                         ],
                         filters: [
                         filters: [
                             {
                             {
@@ -474,6 +486,11 @@ describe('Collection resolver', () => {
                                         )}"]`,
                                         )}"]`,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                     },
                                     },
+                                    {
+                                        name: 'containsAny',
+                                        value: `false`,
+                                        type: ConfigArgType.BOOLEAN,
+                                    },
                                 ],
                                 ],
                             },
                             },
                         ],
                         ],
@@ -483,6 +500,48 @@ describe('Collection resolver', () => {
                     'Instant Camera',
                     'Instant Camera',
                 ]);
                 ]);
             });
             });
+
+            it('photo OR pear', async () => {
+                const result = await client.query<
+                    CreateCollectionSelectVariants.Mutation,
+                    CreateCollectionSelectVariants.Variables
+                    >(CREATE_COLLECTION_SELECT_VARIANTS, {
+                    input: {
+                        translations: [
+                            { languageCode: LanguageCode.en, name: 'Photo OR Pear', description: '' },
+                        ],
+                        filters: [
+                            {
+                                code: facetValueCollectionFilter.code,
+                                arguments: [
+                                    {
+                                        name: 'facetValueIds',
+                                        value: `["${getFacetValueId('pear')}", "${getFacetValueId(
+                                            'photo',
+                                        )}"]`,
+                                        type: ConfigArgType.FACET_VALUE_IDS,
+                                    },
+                                    {
+                                        name: 'containsAny',
+                                        value: `true`,
+                                        type: ConfigArgType.BOOLEAN,
+                                    },
+                                ],
+                            },
+                        ],
+                    } as CreateCollectionInput,
+                });
+                expect(result.createCollection.productVariants.items.map(i => i.name)).toEqual([
+                    'Laptop 13 inch 8GB',
+                    'Laptop 15 inch 8GB',
+                    'Laptop 13 inch 16GB',
+                    'Laptop 15 inch 16GB',
+                    'Instant Camera',
+                    'Camera Lens',
+                    'Tripod',
+                    'SLR Camera',
+                ]);
+            });
         });
         });
 
 
         describe('variantName filter', () => {
         describe('variantName filter', () => {
@@ -493,7 +552,7 @@ describe('Collection resolver', () => {
                 const { createCollection } = await client.query<
                 const { createCollection } = await client.query<
                     CreateCollection.Mutation,
                     CreateCollection.Mutation,
                     CreateCollection.Variables
                     CreateCollection.Variables
-                >(CREATE_COLLECTION, {
+                    >(CREATE_COLLECTION, {
                     input: {
                     input: {
                         translations: [
                         translations: [
                             { languageCode: LanguageCode.en, name: `${operator} ${term}`, description: '' },
                             { languageCode: LanguageCode.en, name: `${operator} ${term}`, description: '' },
@@ -526,7 +585,7 @@ describe('Collection resolver', () => {
                 const result = await client.query<
                 const result = await client.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
                     GetCollectionProducts.Variables
-                >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                    >(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: collection.id,
                     id: collection.id,
                 });
                 });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
@@ -542,7 +601,7 @@ describe('Collection resolver', () => {
                 const result = await client.query<
                 const result = await client.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
                     GetCollectionProducts.Variables
-                >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                    >(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: collection.id,
                     id: collection.id,
                 });
                 });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual(['Camera Lens']);
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual(['Camera Lens']);
@@ -554,7 +613,7 @@ describe('Collection resolver', () => {
                 const result = await client.query<
                 const result = await client.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
                     GetCollectionProducts.Variables
-                >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                    >(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: collection.id,
                     id: collection.id,
                 });
                 });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
@@ -569,7 +628,7 @@ describe('Collection resolver', () => {
                 const result = await client.query<
                 const result = await client.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
                     GetCollectionProducts.Variables
-                >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                    >(GET_COLLECTION_PRODUCT_VARIANTS, {
                     id: collection.id,
                     id: collection.id,
                 });
                 });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
@@ -630,7 +689,7 @@ describe('Collection resolver', () => {
                 const result = await client.query<
                 const result = await client.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
                     GetCollectionProducts.Variables
-                >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
+                    >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                     'Laptop 13 inch 8GB',
                     'Laptop 13 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 15 inch 8GB',
@@ -658,7 +717,7 @@ describe('Collection resolver', () => {
                 const result = await client.query<
                 const result = await client.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
                     GetCollectionProducts.Variables
-                >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
+                    >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                     'Laptop 13 inch 8GB',
                     'Laptop 13 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 15 inch 8GB',
@@ -687,7 +746,7 @@ describe('Collection resolver', () => {
                 const result = await client.query<
                 const result = await client.query<
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Query,
                     GetCollectionProducts.Variables
                     GetCollectionProducts.Variables
-                >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
+                    >(GET_COLLECTION_PRODUCT_VARIANTS, { id: pearCollection.id });
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                 expect(result.collection!.productVariants.items.map(i => i.name)).toEqual([
                     'Laptop 13 inch 8GB',
                     'Laptop 13 inch 8GB',
                     'Laptop 15 inch 8GB',
                     'Laptop 15 inch 8GB',
@@ -707,13 +766,14 @@ describe('Collection resolver', () => {
             const result = await client.query<
             const result = await client.query<
                 GetCollectionsForProducts.Query,
                 GetCollectionsForProducts.Query,
                 GetCollectionsForProducts.Variables
                 GetCollectionsForProducts.Variables
-            >(GET_COLLECTIONS_FOR_PRODUCTS, { term: 'camera' });
+                >(GET_COLLECTIONS_FOR_PRODUCTS, { term: 'camera' });
             expect(result.products.items[0].collections).toEqual([
             expect(result.products.items[0].collections).toEqual([
                 { id: 'T_3', name: 'Electronics' },
                 { id: 'T_3', name: 'Electronics' },
                 { id: 'T_5', name: 'Pear' },
                 { id: 'T_5', name: 'Pear' },
-                { id: 'T_7', name: 'Photo Pear' },
-                { id: 'T_8', name: 'contains camera' },
-                { id: 'T_10', name: 'endsWith camera' },
+                { id: 'T_7', name: 'Photo AND Pear' },
+                { id: 'T_8', name: 'Photo OR Pear' },
+                { id: 'T_9', name: 'contains camera' },
+                { id: 'T_11', name: 'endsWith camera' },
             ]);
             ]);
         });
         });
     });
     });
@@ -725,7 +785,7 @@ describe('Collection resolver', () => {
         const { collection } = await client.query<
         const { collection } = await client.query<
             GetCollectionProducts.Query,
             GetCollectionProducts.Query,
             GetCollectionProducts.Variables
             GetCollectionProducts.Variables
-        >(GET_COLLECTION_PRODUCT_VARIANTS, {
+            >(GET_COLLECTION_PRODUCT_VARIANTS, {
             id: pearCollection.id,
             id: pearCollection.id,
         });
         });
         expect(collection!.productVariants.items.map(i => i.name)).toEqual([
         expect(collection!.productVariants.items.map(i => i.name)).toEqual([

+ 10 - 0
packages/core/e2e/default-search-plugin.e2e-spec.ts

@@ -347,6 +347,11 @@ describe('Default search plugin', () => {
                                         value: `["T_4"]`,
                                         value: `["T_4"]`,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                         type: ConfigArgType.FACET_VALUE_IDS,
                                     },
                                     },
+                                    {
+                                        name: 'containsAny',
+                                        value: `false`,
+                                        type: ConfigArgType.BOOLEAN,
+                                    },
                                 ],
                                 ],
                             },
                             },
                         ],
                         ],
@@ -396,6 +401,11 @@ describe('Default search plugin', () => {
                                     value: `["T_3"]`,
                                     value: `["T_3"]`,
                                     type: ConfigArgType.FACET_VALUE_IDS,
                                     type: ConfigArgType.FACET_VALUE_IDS,
                                 },
                                 },
+                                {
+                                    name: 'containsAny',
+                                    value: `false`,
+                                    type: ConfigArgType.BOOLEAN,
+                                },
                             ],
                             ],
                         },
                         },
                     ],
                     ],

+ 5 - 0
packages/core/e2e/shop-catalog.e2e-spec.ts

@@ -251,6 +251,11 @@ describe('Shop catalog', () => {
                                     value: `["${category.values[3].id}"]`,
                                     value: `["${category.values[3].id}"]`,
                                     type: ConfigArgType.FACET_VALUE_IDS,
                                     type: ConfigArgType.FACET_VALUE_IDS,
                                 },
                                 },
+                                {
+                                    name: 'containsAny',
+                                    value: `false`,
+                                    type: ConfigArgType.BOOLEAN,
+                                },
                             ],
                             ],
                         },
                         },
                     ],
                     ],

+ 2 - 2
packages/core/src/common/configurable-operation.ts

@@ -62,7 +62,7 @@ export function configurableDefToOperation(def: ConfigurableOperationDef): Confi
 export function argsArrayToHash<T>(args: ConfigArg[]): ConfigArgValues<T> {
 export function argsArrayToHash<T>(args: ConfigArg[]): ConfigArgValues<T> {
     const output: ConfigArgValues<T> = {} as any;
     const output: ConfigArgValues<T> = {} as any;
     for (const arg of args) {
     for (const arg of args) {
-        if (arg.value != null) {
+        if (arg && arg.value != null) {
             output[arg.name as keyof ConfigArgValues<T>] = coerceValueToType<T>(arg);
             output[arg.name as keyof ConfigArgValues<T>] = coerceValueToType<T>(arg);
         }
         }
     }
     }
@@ -81,7 +81,7 @@ function coerceValueToType<T>(arg: ConfigArg): ConfigArgValues<T>[keyof T] {
         case ConfigArgType.DATETIME:
         case ConfigArgType.DATETIME:
             return Date.parse(arg.value || '') as any;
             return Date.parse(arg.value || '') as any;
         case ConfigArgType.BOOLEAN:
         case ConfigArgType.BOOLEAN:
-            return !!arg.value as any;
+            return !!(arg.value && (arg.value.toLowerCase() === 'true' || arg.value === '1')) as any;
         case ConfigArgType.FACET_VALUE_IDS:
         case ConfigArgType.FACET_VALUE_IDS:
             try {
             try {
                 return JSON.parse(arg.value as any);
                 return JSON.parse(arg.value as any);

+ 1 - 1
packages/core/src/config/collection/collection-filter.ts

@@ -9,7 +9,7 @@ import {
 } from '../../common/configurable-operation';
 } from '../../common/configurable-operation';
 import { ProductVariant } from '../../entity/product-variant/product-variant.entity';
 import { ProductVariant } from '../../entity/product-variant/product-variant.entity';
 
 
-export type CollectionFilterArgType = ConfigArgType.FACET_VALUE_IDS | ConfigArgType.STRING | ConfigArgType.STRING_OPERATOR;
+export type CollectionFilterArgType = ConfigArgType.FACET_VALUE_IDS | ConfigArgType.STRING | ConfigArgType.STRING_OPERATOR | ConfigArgType.BOOLEAN;
 export type CollectionFilterArgs = ConfigArgs<CollectionFilterArgType>;
 export type CollectionFilterArgs = ConfigArgs<CollectionFilterArgType>;
 
 
 export type ApplyCollectionFilterFn<T extends CollectionFilterArgs> = (
 export type ApplyCollectionFilterFn<T extends CollectionFilterArgs> = (

+ 2 - 1
packages/core/src/config/collection/default-collection-filters.ts

@@ -9,6 +9,7 @@ import { CollectionFilter } from './collection-filter';
 export const facetValueCollectionFilter = new CollectionFilter({
 export const facetValueCollectionFilter = new CollectionFilter({
     args: {
     args: {
         facetValueIds: ConfigArgType.FACET_VALUE_IDS,
         facetValueIds: ConfigArgType.FACET_VALUE_IDS,
+        containsAny: ConfigArgType.BOOLEAN,
     },
     },
     code: 'facet-value-filter',
     code: 'facet-value-filter',
     description: 'Filter by FacetValues',
     description: 'Filter by FacetValues',
@@ -26,7 +27,7 @@ export const facetValueCollectionFilter = new CollectionFilter({
                     }),
                     }),
                 )
                 )
                 .groupBy('productVariant.id')
                 .groupBy('productVariant.id')
-                .having(`COUNT(1) >= :count`, {count: args.facetValueIds.length});
+                .having(`COUNT(1) >= :count`, { count: args.containsAny ? 1 : args.facetValueIds.length });
         } else {
         } else {
             // If no facetValueIds are specified, no ProductVariants will be matched.
             // If no facetValueIds are specified, no ProductVariants will be matched.
             qb.andWhere('1 = 0');
             qb.andWhere('1 = 0');

+ 5 - 0
packages/core/src/data-import/providers/populator/populator.ts

@@ -117,6 +117,11 @@ export class Populator {
                                 type: ConfigArgType.FACET_VALUE_IDS,
                                 type: ConfigArgType.FACET_VALUE_IDS,
                                 value: JSON.stringify(facetValueIds),
                                 value: JSON.stringify(facetValueIds),
                             },
                             },
+                            {
+                                name: 'containsAny',
+                                value: `false`,
+                                type: ConfigArgType.BOOLEAN,
+                            },
                         ],
                         ],
                     },
                     },
                 ],
                 ],

+ 5 - 2
packages/core/src/service/services/collection.service.ts

@@ -386,17 +386,20 @@ export class CollectionService implements OnModuleInit {
 
 
         // Apply any facetValue-based filters
         // Apply any facetValue-based filters
         if (facetFilters.length) {
         if (facetFilters.length) {
+            const [idsArg, containsAnyArg] = facetFilters[0].args;
             const mergedArgs = facetFilters
             const mergedArgs = facetFilters
                 .map(f => f.args[0].value)
                 .map(f => f.args[0].value)
                 .filter(notNullOrUndefined)
                 .filter(notNullOrUndefined)
                 .map(value => JSON.parse(value))
                 .map(value => JSON.parse(value))
                 .reduce((all, ids) => [...all, ...ids]);
                 .reduce((all, ids) => [...all, ...ids]);
+
             qb = facetValueCollectionFilter.apply(qb, [
             qb = facetValueCollectionFilter.apply(qb, [
                 {
                 {
-                    name: facetFilters[0].args[0].name,
-                    type: facetFilters[0].args[0].type,
+                    name: idsArg.name,
+                    type: idsArg.type,
                     value: JSON.stringify(Array.from(new Set(mergedArgs))),
                     value: JSON.stringify(Array.from(new Set(mergedArgs))),
                 },
                 },
+                containsAnyArg,
             ]);
             ]);
         }
         }