diff --git a/packages/core/e2e/fixtures/test-plugins/list-query-plugin.ts b/packages/core/e2e/fixtures/test-plugins/list-query-plugin.ts index 5be10c4ba6..32e60ddfaf 100644 --- a/packages/core/e2e/fixtures/test-plugins/list-query-plugin.ts +++ b/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) { diff --git a/packages/core/e2e/list-query-builder.e2e-spec.ts b/packages/core/e2e/list-query-builder.e2e-spec.ts index 50d9b9c027..5454d79be6 100644 --- a/packages/core/e2e/list-query-builder.e2e-spec.ts +++ b/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']); }); }); }); diff --git a/packages/core/e2e/product.e2e-spec.ts b/packages/core/e2e/product.e2e-spec.ts index 934c67cc5d..9edb5315dc 100644 --- a/packages/core/e2e/product.e2e-spec.ts +++ b/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); + }); }); }); diff --git a/packages/core/src/service/helpers/list-query-builder/list-query-builder.ts b/packages/core/src/service/helpers/list-query-builder/list-query-builder.ts index da7124dd3e..561cc87a51 100644 --- a/packages/core/src/service/helpers/list-query-builder/list-query-builder.ts +++ b/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, + }); }), ); }