Procházet zdrojové kódy

feat(core): Add ability to get variants for a specific product in productVariants query

#786
Dmytro Kostenko před 4 roky
rodič
revize
1da0592b18

+ 2 - 1
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -63,7 +63,7 @@ export type Query = {
   productOptionGroups: Array<ProductOptionGroup>;
   /** Get a ProductVariant by id */
   productVariant?: Maybe<ProductVariant>;
-  /** List ProductVariants */
+  /** List ProductVariants either all or for the specific product. */
   productVariants: ProductVariantList;
   /** List Products */
   products: ProductList;
@@ -227,6 +227,7 @@ export type QueryProductVariantArgs = {
 
 export type QueryProductVariantsArgs = {
   options?: Maybe<ProductVariantListOptions>;
+  productId?: Maybe<Scalars['ID']>;
 };
 
 

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 509 - 654
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts


+ 2 - 1
packages/common/src/generated-types.ts

@@ -61,7 +61,7 @@ export type Query = {
   products: ProductList;
   /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */
   product?: Maybe<Product>;
-  /** List ProductVariants */
+  /** List ProductVariants either all or for the specific product. */
   productVariants: ProductVariantList;
   /** Get a ProductVariant by id */
   productVariant?: Maybe<ProductVariant>;
@@ -228,6 +228,7 @@ export type QueryProductArgs = {
 
 export type QueryProductVariantsArgs = {
   options?: Maybe<ProductVariantListOptions>;
+  productId?: Maybe<Scalars['ID']>;
 };
 
 

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 509 - 654
packages/core/e2e/graphql/generated-e2e-admin-types.ts


+ 41 - 2
packages/core/e2e/product.e2e-spec.ts

@@ -511,6 +511,45 @@ describe('Product resolver', () => {
                 },
             ]);
         });
+
+        it('returns variants for particular product by id', async () => {
+            const { productVariants } = await adminClient.query<
+                GetProductVariantList.Query,
+                GetProductVariantList.Variables
+            >(GET_PRODUCT_VARIANT_LIST, {
+                options: {
+                    take: 3,
+                    sort: {
+                        price: SortOrder.ASC,
+                    },
+                },
+                productId: 'T_1',
+            });
+
+            expect(productVariants.items).toEqual([
+                {
+                    id: 'T_1',
+                    name: 'Laptop 13 inch 8GB',
+                    price: 129900,
+                    priceWithTax: 155880,
+                    sku: 'L2201308',
+                },
+                {
+                    id: 'T_2',
+                    name: 'Laptop 15 inch 8GB',
+                    price: 139900,
+                    priceWithTax: 167880,
+                    sku: 'L2201508',
+                },
+                {
+                    id: 'T_3',
+                    name: 'Laptop 13 inch 16GB',
+                    priceWithTax: 263880,
+                    price: 219900,
+                    sku: 'L2201316',
+                },
+            ]);
+        });
     });
 
     describe('productVariant query', () => {
@@ -1440,8 +1479,8 @@ export const GET_PRODUCT_VARIANT = gql`
 `;
 
 export const GET_PRODUCT_VARIANT_LIST = gql`
-    query GetProductVariantLIST($options: ProductVariantListOptions) {
-        productVariants(options: $options) {
+    query GetProductVariantLIST($options: ProductVariantListOptions, $productId: ID) {
+        productVariants(options: $options, productId: $productId) {
             items {
                 id
                 name

+ 8 - 0
packages/core/src/api/resolvers/admin/product.resolver.ts

@@ -77,6 +77,14 @@ export class ProductResolver {
         @Ctx() ctx: RequestContext,
         @Args() args: QueryProductVariantsArgs,
     ): Promise<PaginatedList<Translated<ProductVariant>>> {
+        if (args.productId) {
+            return this.productVariantService.getVariantsByProductId(
+                ctx,
+                args.productId,
+                args.options || undefined,
+            );
+        }
+
         return this.productVariantService.findAll(ctx, args.options || undefined);
     }
 

+ 1 - 1
packages/core/src/api/resolvers/entity/product-entity.resolver.ts

@@ -53,7 +53,7 @@ export class ProductEntityResolver {
         @Parent() product: Product,
         @Api() apiType: ApiType,
     ): Promise<Array<Translated<ProductVariant>>> {
-        const variants = await this.productVariantService.getVariantsByProductId(ctx, product.id);
+        const { items: variants } = await this.productVariantService.getVariantsByProductId(ctx, product.id);
         return variants.filter(v => (apiType === 'admin' ? true : v.enabled));
     }
 

+ 2 - 2
packages/core/src/api/schema/admin-api/product.api.graphql

@@ -3,8 +3,8 @@ type Query {
     products(options: ProductListOptions): ProductList!
     "Get a Product either by id or slug. If neither id nor slug is speicified, an error will result."
     product(id: ID, slug: String): Product
-    "List ProductVariants"
-    productVariants(options: ProductVariantListOptions): ProductVariantList!
+    "List ProductVariants either all or for the specific product."
+    productVariants(options: ProductVariantListOptions, productId: ID): ProductVariantList!
     "Get a ProductVariant by id"
     productVariant(id: ID!): ProductVariant
 }

+ 33 - 24
packages/core/src/service/services/product-variant.service.ts

@@ -130,8 +130,11 @@ export class ProductVariantService {
             });
     }
 
-    getVariantsByProductId(ctx: RequestContext, productId: ID): Promise<Array<Translated<ProductVariant>>> {
-        const qb = this.connection.getRepository(ctx, ProductVariant).createQueryBuilder('productVariant');
+    getVariantsByProductId(
+        ctx: RequestContext,
+        productId: ID,
+        options: ListQueryOptions<ProductVariant> = {},
+    ): Promise<PaginatedList<Translated<ProductVariant>>> {
         const relations = [
             'options',
             'facetValues',
@@ -140,32 +143,38 @@ export class ProductVariantService {
             'assets',
             'featuredAsset',
         ];
-        FindOptionsUtils.applyFindManyOptionsOrConditionsToQueryBuilder(qb, { relations });
-        // tslint:disable-next-line:no-non-null-assertion
-        FindOptionsUtils.joinEagerRelations(qb, qb.alias, qb.expressionMap.mainAlias!.metadata);
-        return qb
-            .innerJoinAndSelect('productVariant.channels', 'channel', 'channel.id = :channelId', {
+
+        return this.listQueryBuilder
+            .build(ProductVariant, options, {
+                relations,
+                orderBy: { id: 'ASC' },
+                where: { deletedAt: null },
+                ctx,
+            })
+            .innerJoinAndSelect('productvariant.channels', 'channel', 'channel.id = :channelId', {
                 channelId: ctx.channelId,
             })
-            .innerJoinAndSelect('productVariant.product', 'product', 'product.id = :productId', {
+            .innerJoinAndSelect('productvariant.product', 'product', 'product.id = :productId', {
                 productId,
             })
-            .andWhere('productVariant.deletedAt IS NULL')
-            .orderBy('productVariant.id', 'ASC')
-            .getMany()
-            .then(
-                async variants =>
-                    await Promise.all(
-                        variants.map(async variant => {
-                            const variantWithPrices = await this.applyChannelPriceAndTax(variant, ctx);
-                            return translateDeep(variantWithPrices, ctx.languageCode, [
-                                'options',
-                                'facetValues',
-                                ['facetValues', 'facet'],
-                            ]);
-                        }),
-                    ),
-            );
+            .getManyAndCount()
+            .then(async ([variants, totalItems]) => {
+                const items = await Promise.all(
+                    variants.map(async variant => {
+                        const variantWithPrices = await this.applyChannelPriceAndTax(variant, ctx);
+                        return translateDeep(variantWithPrices, ctx.languageCode, [
+                            'options',
+                            'facetValues',
+                            ['facetValues', 'facet'],
+                        ]);
+                    }),
+                );
+
+                return {
+                    items,
+                    totalItems,
+                };
+            });
     }
 
     getVariantsByCollectionId(

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 509 - 654
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts


+ 6 - 4
packages/elasticsearch-plugin/src/indexer.controller.ts

@@ -109,7 +109,7 @@ export class ElasticsearchIndexerController implements OnModuleInit, OnModuleDes
         }
         const channelIds = product.channels.map(c => c.id);
         await this.deleteProductInternal(product, channelIds);
-        const variants = await this.productVariantService.getVariantsByProductId(ctx, productId);
+        const { items: variants } = await this.productVariantService.getVariantsByProductId(ctx, productId);
         await this.deleteVariantsInternal(variants, channelIds);
         return true;
     }
@@ -124,7 +124,7 @@ export class ElasticsearchIndexerController implements OnModuleInit, OnModuleDes
     }: ProductChannelMessageData): Promise<boolean> {
         const ctx = RequestContext.deserialize(rawContext);
         await this.updateProductInternal(ctx, productId);
-        const variants = await this.productVariantService.getVariantsByProductId(ctx, productId);
+        const { items: variants } = await this.productVariantService.getVariantsByProductId(ctx, productId);
         await this.updateVariantsInternal(
             ctx,
             variants.map(v => v.id),
@@ -147,7 +147,7 @@ export class ElasticsearchIndexerController implements OnModuleInit, OnModuleDes
             return false;
         }
         await this.deleteProductInternal(product, [channelId]);
-        const variants = await this.productVariantService.getVariantsByProductId(ctx, productId);
+        const { items: variants } = await this.productVariantService.getVariantsByProductId(ctx, productId);
         await this.deleteVariantsInternal(variants, [channelId]);
         return true;
     }
@@ -198,7 +198,9 @@ export class ElasticsearchIndexerController implements OnModuleInit, OnModuleDes
         for (const productId of productIds) {
             await this.updateProductInternal(ctx, productId);
         }
-        const channelIds = unique(variants.reduce((flat: ID[], v) => [...flat, ...v.channels.map(c => c.id)], []));
+        const channelIds = unique(
+            variants.reduce((flat: ID[], v) => [...flat, ...v.channels.map(c => c.id)], []),
+        );
         await this.deleteVariantsInternal(variants, channelIds);
         return true;
     }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
schema-admin.json


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů