Browse Source

fix(core): Correct handling of non-default languages in ListQueryBuilder

Fixes #980
Michael Bromley 4 years ago
parent
commit
837840ea05

+ 8 - 0
packages/core/e2e/fixtures/test-plugins/list-query-plugin.ts

@@ -214,6 +214,13 @@ export class ListQueryPlugin implements OnApplicationBootstrap {
                     active: false,
                     order: 4,
                 }),
+                new TestEntity({
+                    label: 'F',
+                    description: 'quis nostrud exercitation ullamco', // 33
+                    date: new Date('2020-02-07T10:00:00.000Z'),
+                    active: false,
+                    order: 5,
+                }),
             ]);
 
             const translations: any = {
@@ -222,6 +229,7 @@ export class ListQueryPlugin implements OnApplicationBootstrap {
                 C: { [LanguageCode.en]: 'cake', [LanguageCode.de]: 'kuchen' },
                 D: { [LanguageCode.en]: 'dog', [LanguageCode.de]: 'hund' },
                 E: { [LanguageCode.en]: 'egg' },
+                F: { [LanguageCode.de]: 'baum' },
             };
 
             for (const testEntity of testEntities) {

+ 50 - 34
packages/core/e2e/list-query-builder.e2e-spec.ts

@@ -51,14 +51,15 @@ describe('ListQueryBuilder', () => {
                 { languageCode: LanguageCode.en },
             );
 
-            expect(testEntities.totalItems).toBe(5);
-            expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E']);
+            expect(testEntities.totalItems).toBe(6);
+            expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
             expect(testEntities.items.map((i: any) => i.name)).toEqual([
                 'apple',
                 'bike',
                 'cake',
                 'dog',
                 'egg',
+                'baum', // if default en lang does not exist, use next available lang
             ]);
         });
 
@@ -71,14 +72,15 @@ describe('ListQueryBuilder', () => {
                 { languageCode: LanguageCode.de },
             );
 
-            expect(testEntities.totalItems).toBe(5);
-            expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E']);
+            expect(testEntities.totalItems).toBe(6);
+            expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'C', 'D', 'E', 'F']);
             expect(testEntities.items.map((i: any) => i.name)).toEqual([
                 'apfel',
                 'fahrrad',
                 'kuchen',
                 'hund',
                 'egg', // falls back to en translation when de doesn't exist
+                'baum',
             ]);
         });
 
@@ -89,7 +91,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(testEntities.totalItems).toBe(5);
+            expect(testEntities.totalItems).toBe(6);
             expect(getItemLabels(testEntities.items)).toEqual(['A', 'B']);
         });
 
@@ -100,8 +102,8 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(testEntities.totalItems).toBe(5);
-            expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
+            expect(testEntities.totalItems).toBe(6);
+            expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E', 'F']);
         });
 
         it('skip negative is ignored', async () => {
@@ -111,8 +113,8 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(testEntities.totalItems).toBe(5);
-            expect(testEntities.items.length).toBe(5);
+            expect(testEntities.totalItems).toBe(6);
+            expect(testEntities.items.length).toBe(6);
         });
 
         it('take zero is ignored', async () => {
@@ -122,8 +124,8 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(testEntities.totalItems).toBe(5);
-            expect(testEntities.items.length).toBe(5);
+            expect(testEntities.totalItems).toBe(6);
+            expect(testEntities.items.length).toBe(6);
         });
 
         it('take negative is ignored', async () => {
@@ -133,8 +135,8 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(testEntities.totalItems).toBe(5);
-            expect(testEntities.items.length).toBe(5);
+            expect(testEntities.totalItems).toBe(6);
+            expect(testEntities.items.length).toBe(6);
         });
 
         it(
@@ -186,7 +188,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'D', 'E']);
+            expect(getItemLabels(testEntities.items)).toEqual(['A', 'C', 'D', 'E', 'F']);
         });
 
         it('contains', async () => {
@@ -214,7 +216,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'E']);
+            expect(getItemLabels(testEntities.items)).toEqual(['A', 'B', 'E', 'F']);
         });
 
         it('in', async () => {
@@ -242,7 +244,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'E']);
+            expect(getItemLabels(testEntities.items)).toEqual(['B', 'D', 'E', 'F']);
         });
 
         describe('regex', () => {
@@ -330,7 +332,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(getItemLabels(testEntities.items)).toEqual(['C', 'E']);
+            expect(getItemLabels(testEntities.items)).toEqual(['C', 'E', 'F']);
         });
     });
 
@@ -388,7 +390,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
+            expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E', 'F']);
         });
 
         it('gte', async () => {
@@ -402,7 +404,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(getItemLabels(testEntities.items)).toEqual(['B', 'C', 'D', 'E']);
+            expect(getItemLabels(testEntities.items)).toEqual(['B', 'C', 'D', 'E', 'F']);
         });
 
         it('between', async () => {
@@ -463,7 +465,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
+            expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E', 'F']);
         });
 
         it('after on same date', async () => {
@@ -477,7 +479,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E']);
+            expect(getItemLabels(testEntities.items)).toEqual(['C', 'D', 'E', 'F']);
         });
 
         it('between', async () => {
@@ -508,7 +510,7 @@ describe('ListQueryBuilder', () => {
                 },
             });
 
-            expect(testEntities.items.map((x: any) => x.label)).toEqual(['E', 'D', 'C', 'B', 'A']);
+            expect(testEntities.items.map((x: any) => x.label)).toEqual(['F', 'E', 'D', 'C', 'B', 'A']);
         });
 
         it('sort by number', async () => {
@@ -519,7 +521,7 @@ describe('ListQueryBuilder', () => {
                     },
                 },
             });
-            expect(testEntities.items.map((x: any) => x.label)).toEqual(['E', 'D', 'C', 'B', 'A']);
+            expect(testEntities.items.map((x: any) => x.label)).toEqual(['F', 'E', 'D', 'C', 'B', 'A']);
         });
 
         it('sort by date', async () => {
@@ -530,7 +532,7 @@ describe('ListQueryBuilder', () => {
                     },
                 },
             });
-            expect(testEntities.items.map((x: any) => x.label)).toEqual(['E', 'D', 'C', 'B', 'A']);
+            expect(testEntities.items.map((x: any) => x.label)).toEqual(['F', 'E', 'D', 'C', 'B', 'A']);
         });
 
         it('sort by ID', async () => {
@@ -541,7 +543,7 @@ describe('ListQueryBuilder', () => {
                     },
                 },
             });
-            expect(testEntities.items.map((x: any) => x.label)).toEqual(['E', 'D', 'C', 'B', 'A']);
+            expect(testEntities.items.map((x: any) => x.label)).toEqual(['F', 'E', 'D', 'C', 'B', 'A']);
         });
 
         it('sort by translated field en', async () => {
@@ -554,6 +556,7 @@ describe('ListQueryBuilder', () => {
             });
             expect(testEntities.items.map((x: any) => x.name)).toEqual([
                 'apple',
+                'baum', // falling back to de here
                 'bike',
                 'cake',
                 'dog',
@@ -575,6 +578,7 @@ describe('ListQueryBuilder', () => {
             );
             expect(testEntities.items.map((x: any) => x.name)).toEqual([
                 'apfel',
+                'baum',
                 'egg',
                 'fahrrad',
                 'hund',
@@ -588,10 +592,10 @@ describe('ListQueryBuilder', () => {
                     sort: {
                         name: SortOrder.ASC,
                     },
-                    take: 3,
+                    take: 4,
                 },
             });
-            expect(testEntities.items.map((x: any) => x.name)).toEqual(['apple', 'bike', 'cake']);
+            expect(testEntities.items.map((x: any) => x.name)).toEqual(['apple', 'baum', 'bike', 'cake']);
         });
 
         it('sort by translated field de with take', async () => {
@@ -602,12 +606,12 @@ describe('ListQueryBuilder', () => {
                         sort: {
                             name: SortOrder.ASC,
                         },
-                        take: 3,
+                        take: 4,
                     },
                 },
                 { languageCode: LanguageCode.de },
             );
-            expect(testEntities.items.map((x: any) => x.name)).toEqual(['apfel', 'egg', 'fahrrad']);
+            expect(testEntities.items.map((x: any) => x.name)).toEqual(['apfel', 'baum', 'egg', 'fahrrad']);
         });
     });
 
@@ -646,7 +650,7 @@ describe('ListQueryBuilder', () => {
                     },
                 },
             });
-            expect(testEntities.items.map((x: any) => x.label)).toEqual(['B', 'A', 'E', 'D', 'C']);
+            expect(testEntities.items.map((x: any) => x.label)).toEqual(['B', 'A', 'E', 'D', 'C', 'F']);
         });
 
         it('sort by calculated property with join', async () => {
@@ -657,7 +661,7 @@ describe('ListQueryBuilder', () => {
                     },
                 },
             });
-            expect(testEntities.items.map((x: any) => x.label)).toEqual(['B', 'A', 'E', 'D', 'C']);
+            expect(testEntities.items.map((x: any) => x.label)).toEqual(['B', 'A', 'E', 'D', 'C', 'F']);
         });
     });
 
@@ -675,7 +679,13 @@ describe('ListQueryBuilder', () => {
                     },
                 },
             });
-            expect(testEntities.items.map((x: any) => x.name)).toEqual(['bike', 'cake', 'dog', 'egg']);
+            expect(testEntities.items.map((x: any) => x.name)).toEqual([
+                'baum',
+                'bike',
+                'cake',
+                'dog',
+                'egg',
+            ]);
         });
 
         it('sort by translated field de & filter', async () => {
@@ -695,7 +705,13 @@ describe('ListQueryBuilder', () => {
                 },
                 { languageCode: LanguageCode.de },
             );
-            expect(testEntities.items.map((x: any) => x.name)).toEqual(['egg', 'fahrrad', 'hund', 'kuchen']);
+            expect(testEntities.items.map((x: any) => x.name)).toEqual([
+                'baum',
+                'egg',
+                'fahrrad',
+                'hund',
+                'kuchen',
+            ]);
         });
 
         it('sort by translated field de & filter & pagination', async () => {
@@ -717,7 +733,7 @@ describe('ListQueryBuilder', () => {
                 },
                 { languageCode: LanguageCode.de },
             );
-            expect(testEntities.items.map((x: any) => x.name)).toEqual(['fahrrad', 'hund']);
+            expect(testEntities.items.map((x: any) => x.name)).toEqual(['egg', 'fahrrad']);
         });
     });
 });

+ 49 - 0
packages/core/e2e/product.e2e-spec.ts

@@ -1493,6 +1493,55 @@ describe('Product resolver', () => {
                     deletedVariant.options.map(o => o.code).sort(),
                 );
             });
+
+            // https://github.com/vendure-ecommerce/vendure/issues/980
+            it('creating variants in a non-default language', async () => {
+                const { createProduct } = await adminClient.query<
+                    CreateProduct.Mutation,
+                    CreateProduct.Variables
+                >(CREATE_PRODUCT, {
+                    input: {
+                        translations: [
+                            {
+                                languageCode: LanguageCode.de,
+                                name: 'Ananas',
+                                slug: 'ananas',
+                                description: 'Yummy Ananas',
+                            },
+                        ],
+                    },
+                });
+
+                const { createProductVariants } = await adminClient.query<
+                    CreateProductVariants.Mutation,
+                    CreateProductVariants.Variables
+                >(CREATE_PRODUCT_VARIANTS, {
+                    input: [
+                        {
+                            productId: createProduct.id,
+                            sku: 'AN1110111',
+                            optionIds: [],
+                            translations: [{ languageCode: LanguageCode.de, name: 'Ananas Klein' }],
+                        },
+                    ],
+                });
+
+                expect(createProductVariants.length).toBe(1);
+                expect(createProductVariants[0]?.name).toBe('Ananas Klein');
+
+                const { product } = await adminClient.query<
+                    GetProductWithVariants.Query,
+                    GetProductWithVariants.Variables
+                >(
+                    GET_PRODUCT_WITH_VARIANTS,
+                    {
+                        id: createProduct.id,
+                    },
+                    { languageCode: LanguageCode.en },
+                );
+
+                expect(product?.variants.length).toBe(1);
+            });
         });
     });
 

+ 22 - 4
packages/core/src/service/helpers/list-query-builder/list-query-builder.ts

@@ -253,11 +253,29 @@ export class ListQueryBuilder implements OnApplicationBootstrap {
                                 );
                             }),
                         );
-                        qb.setParameters({
-                            nonDefaultLanguageCode: languageCode,
-                            defaultLanguageCode: this.configService.defaultLanguageCode,
-                        });
+                    } else {
+                        qb1.orWhere(
+                            new Brackets(qb2 => {
+                                const translationEntity = translationColumns[0].entityMetadata.target;
+                                const subQb1 = this.connection.rawConnection
+                                    .createQueryBuilder(translationEntity, 'translation')
+                                    .where(`translation.base = ${alias}.id`)
+                                    .andWhere('translation.languageCode = :defaultLanguageCode');
+                                const subQb2 = this.connection.rawConnection
+                                    .createQueryBuilder(translationEntity, 'translation')
+                                    .where(`translation.base = ${alias}.id`)
+                                    .andWhere('translation.languageCode != :defaultLanguageCode');
+
+                                qb2.where(`NOT EXISTS (${subQb1.getQuery()})`).andWhere(
+                                    `EXISTS (${subQb2.getQuery()})`,
+                                );
+                            }),
+                        );
                     }
+                    qb.setParameters({
+                        nonDefaultLanguageCode: languageCode,
+                        defaultLanguageCode: this.configService.defaultLanguageCode,
+                    });
                 }),
             );
         }