Browse Source

fix(core): Correct handling of nested variantNameCollectionFilters

Fixes #927
Michael Bromley 4 years ago
parent
commit
14b40bbb9b

+ 38 - 1
packages/core/e2e/collection.e2e-spec.ts

@@ -1286,12 +1286,14 @@ describe('Collection resolver', () => {
             async function createVariantNameFilteredCollection(
             async function createVariantNameFilteredCollection(
                 operator: string,
                 operator: string,
                 term: string,
                 term: string,
+                parentId?: string,
             ): Promise<Collection.Fragment> {
             ): Promise<Collection.Fragment> {
                 const { createCollection } = await adminClient.query<
                 const { createCollection } = await adminClient.query<
                     CreateCollection.Mutation,
                     CreateCollection.Mutation,
                     CreateCollection.Variables
                     CreateCollection.Variables
                 >(CREATE_COLLECTION, {
                 >(CREATE_COLLECTION, {
                     input: {
                     input: {
+                        parentId,
                         translations: [
                         translations: [
                             {
                             {
                                 languageCode: LanguageCode.en,
                                 languageCode: LanguageCode.en,
@@ -1395,6 +1397,41 @@ describe('Collection resolver', () => {
                     'Hat',
                     'Hat',
                 ]);
                 ]);
             });
             });
+
+            // https://github.com/vendure-ecommerce/vendure/issues/927
+            it('nested variantName filter', async () => {
+                const parent = await createVariantNameFilteredCollection('contains', 'lap');
+
+                const parentResult = await adminClient.query<
+                    GetCollectionProducts.Query,
+                    GetCollectionProducts.Variables
+                >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                    id: parent.id,
+                });
+
+                expect(parentResult.collection?.productVariants.items.map(i => i.name)).toEqual([
+                    'Laptop 13 inch 8GB',
+                    'Laptop 15 inch 8GB',
+                    'Laptop 13 inch 16GB',
+                    'Laptop 15 inch 16GB',
+                ]);
+
+                const child = await createVariantNameFilteredCollection('contains', 'GB', parent.id);
+
+                const childResult = await adminClient.query<
+                    GetCollectionProducts.Query,
+                    GetCollectionProducts.Variables
+                >(GET_COLLECTION_PRODUCT_VARIANTS, {
+                    id: child.id,
+                });
+
+                expect(childResult.collection?.productVariants.items.map(i => i.name)).toEqual([
+                    'Laptop 13 inch 8GB',
+                    'Laptop 15 inch 8GB',
+                    'Laptop 13 inch 16GB',
+                    'Laptop 15 inch 16GB',
+                ]);
+            });
         });
         });
 
 
         describe('re-evaluation of contents on changes', () => {
         describe('re-evaluation of contents on changes', () => {
@@ -1606,7 +1643,7 @@ describe('Collection resolver', () => {
                     name: 'endsWith camera',
                     name: 'endsWith camera',
                 },
                 },
                 {
                 {
-                    id: 'T_21',
+                    id: 'T_23',
                     name: 'pear electronics',
                     name: 'pear electronics',
                 },
                 },
             ]);
             ]);

+ 21 - 5
packages/core/src/config/catalog/default-collection-filters.ts

@@ -1,4 +1,5 @@
 import { LanguageCode } from '@vendure/common/lib/generated-types';
 import { LanguageCode } from '@vendure/common/lib/generated-types';
+import nanoid from 'nanoid';
 
 
 import { UserInputError } from '../../common/error/errors';
 import { UserInputError } from '../../common/error/errors';
 import { ProductVariant } from '../../entity/product-variant/product-variant.entity';
 import { ProductVariant } from '../../entity/product-variant/product-variant.entity';
@@ -88,17 +89,32 @@ export const variantNameCollectionFilter = new CollectionFilter({
     code: 'variant-name-filter',
     code: 'variant-name-filter',
     description: [{ languageCode: LanguageCode.en, value: 'Filter by ProductVariant name' }],
     description: [{ languageCode: LanguageCode.en, value: 'Filter by ProductVariant name' }],
     apply: (qb, args) => {
     apply: (qb, args) => {
-        qb.leftJoin('productVariant.translations', 'translation');
+        const translationAlias = `variant_name_filter_translation`;
+        const termName = `term_${nanoid(6)}`;
+        const hasJoinOnTranslations = !!qb.expressionMap.joinAttributes.find(
+            ja => ja.entityOrProperty === 'productVariant.translations',
+        );
+        if (!hasJoinOnTranslations) {
+            qb.leftJoin('productVariant.translations', translationAlias);
+        }
         const LIKE = qb.connection.options.type === 'postgres' ? 'ILIKE' : 'LIKE';
         const LIKE = qb.connection.options.type === 'postgres' ? 'ILIKE' : 'LIKE';
         switch (args.operator) {
         switch (args.operator) {
             case 'contains':
             case 'contains':
-                return qb.andWhere(`translation.name ${LIKE} :term`, { term: `%${args.term}%` });
+                return qb.andWhere(`${translationAlias}.name ${LIKE} :${termName}`, {
+                    [termName]: `%${args.term}%`,
+                });
             case 'doesNotContain':
             case 'doesNotContain':
-                return qb.andWhere(`translation.name NOT ${LIKE} :term`, { term: `%${args.term}%` });
+                return qb.andWhere(`${translationAlias}.name NOT ${LIKE} :${termName}`, {
+                    [termName]: `%${args.term}%`,
+                });
             case 'startsWith':
             case 'startsWith':
-                return qb.andWhere(`translation.name ${LIKE} :term`, { term: `${args.term}%` });
+                return qb.andWhere(`${translationAlias}.name ${LIKE} :${termName}`, {
+                    [termName]: `${args.term}%`,
+                });
             case 'endsWith':
             case 'endsWith':
-                return qb.andWhere(`translation.name ${LIKE} :term`, { term: `%${args.term}` });
+                return qb.andWhere(`${translationAlias}.name ${LIKE} :${termName}`, {
+                    [termName]: `%${args.term}`,
+                });
             default:
             default:
                 throw new UserInputError(`${args.operator} is not a valid operator`);
                 throw new UserInputError(`${args.operator} is not a valid operator`);
         }
         }