diff --git a/packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts b/packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts index daaf77e036..655082eb27 100644 --- a/packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts +++ b/packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts @@ -56,6 +56,8 @@ export type Query = { /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */ product?: Maybe; /** Get a ProductVariant by id */ + productVariants: ProductVariantList; + /** Get a ProductVariant by id */ productVariant?: Maybe; promotion?: Maybe; promotions: PromotionList; @@ -188,6 +190,10 @@ export type QueryProductArgs = { slug?: Maybe; }; +export type QueryProductVariantsArgs = { + options?: Maybe; +}; + export type QueryProductVariantArgs = { id: Scalars['ID']; }; @@ -3948,6 +3954,13 @@ export type ProductListOptions = { filter?: Maybe; }; +export type ProductVariantListOptions = { + skip?: Maybe; + take?: Maybe; + sort?: Maybe; + filter?: Maybe; +}; + export type PromotionListOptions = { skip?: Maybe; take?: Maybe; @@ -3976,13 +3989,6 @@ export type TaxRateListOptions = { filter?: Maybe; }; -export type ProductVariantListOptions = { - skip?: Maybe; - take?: Maybe; - sort?: Maybe; - filter?: Maybe; -}; - export type HistoryEntryListOptions = { skip?: Maybe; take?: Maybe; @@ -4209,6 +4215,38 @@ export type ProductSortParameter = { description?: Maybe; }; +export type ProductVariantFilterParameter = { + enabled?: Maybe; + trackInventory?: Maybe; + stockOnHand?: Maybe; + stockAllocated?: Maybe; + outOfStockThreshold?: Maybe; + useGlobalOutOfStockThreshold?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + languageCode?: Maybe; + sku?: Maybe; + name?: Maybe; + price?: Maybe; + currencyCode?: Maybe; + priceIncludesTax?: Maybe; + priceWithTax?: Maybe; +}; + +export type ProductVariantSortParameter = { + stockOnHand?: Maybe; + stockAllocated?: Maybe; + outOfStockThreshold?: Maybe; + id?: Maybe; + productId?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + sku?: Maybe; + name?: Maybe; + price?: Maybe; + priceWithTax?: Maybe; +}; + export type PromotionFilterParameter = { createdAt?: Maybe; updatedAt?: Maybe; @@ -4281,38 +4319,6 @@ export type TaxRateSortParameter = { value?: Maybe; }; -export type ProductVariantFilterParameter = { - enabled?: Maybe; - trackInventory?: Maybe; - stockOnHand?: Maybe; - stockAllocated?: Maybe; - outOfStockThreshold?: Maybe; - useGlobalOutOfStockThreshold?: Maybe; - createdAt?: Maybe; - updatedAt?: Maybe; - languageCode?: Maybe; - sku?: Maybe; - name?: Maybe; - price?: Maybe; - currencyCode?: Maybe; - priceIncludesTax?: Maybe; - priceWithTax?: Maybe; -}; - -export type ProductVariantSortParameter = { - stockOnHand?: Maybe; - stockAllocated?: Maybe; - outOfStockThreshold?: Maybe; - id?: Maybe; - productId?: Maybe; - createdAt?: Maybe; - updatedAt?: Maybe; - sku?: Maybe; - name?: Maybe; - price?: Maybe; - priceWithTax?: Maybe; -}; - export type HistoryEntryFilterParameter = { isPublic?: Maybe; createdAt?: Maybe; diff --git a/packages/common/src/generated-types.ts b/packages/common/src/generated-types.ts index a409b531c6..cbdee79bb6 100644 --- a/packages/common/src/generated-types.ts +++ b/packages/common/src/generated-types.ts @@ -57,6 +57,8 @@ export type Query = { /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */ product?: Maybe; /** Get a ProductVariant by id */ + productVariants: ProductVariantList; + /** Get a ProductVariant by id */ productVariant?: Maybe; promotion?: Maybe; promotions: PromotionList; @@ -217,6 +219,11 @@ export type QueryProductArgs = { }; +export type QueryProductVariantsArgs = { + options?: Maybe; +}; + + export type QueryProductVariantArgs = { id: Scalars['ID']; }; @@ -4186,6 +4193,13 @@ export type ProductListOptions = { filter?: Maybe; }; +export type ProductVariantListOptions = { + skip?: Maybe; + take?: Maybe; + sort?: Maybe; + filter?: Maybe; +}; + export type PromotionListOptions = { skip?: Maybe; take?: Maybe; @@ -4214,13 +4228,6 @@ export type TaxRateListOptions = { filter?: Maybe; }; -export type ProductVariantListOptions = { - skip?: Maybe; - take?: Maybe; - sort?: Maybe; - filter?: Maybe; -}; - export type HistoryEntryListOptions = { skip?: Maybe; take?: Maybe; @@ -4447,6 +4454,38 @@ export type ProductSortParameter = { description?: Maybe; }; +export type ProductVariantFilterParameter = { + enabled?: Maybe; + trackInventory?: Maybe; + stockOnHand?: Maybe; + stockAllocated?: Maybe; + outOfStockThreshold?: Maybe; + useGlobalOutOfStockThreshold?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + languageCode?: Maybe; + sku?: Maybe; + name?: Maybe; + price?: Maybe; + currencyCode?: Maybe; + priceIncludesTax?: Maybe; + priceWithTax?: Maybe; +}; + +export type ProductVariantSortParameter = { + stockOnHand?: Maybe; + stockAllocated?: Maybe; + outOfStockThreshold?: Maybe; + id?: Maybe; + productId?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + sku?: Maybe; + name?: Maybe; + price?: Maybe; + priceWithTax?: Maybe; +}; + export type PromotionFilterParameter = { createdAt?: Maybe; updatedAt?: Maybe; @@ -4519,38 +4558,6 @@ export type TaxRateSortParameter = { value?: Maybe; }; -export type ProductVariantFilterParameter = { - enabled?: Maybe; - trackInventory?: Maybe; - stockOnHand?: Maybe; - stockAllocated?: Maybe; - outOfStockThreshold?: Maybe; - useGlobalOutOfStockThreshold?: Maybe; - createdAt?: Maybe; - updatedAt?: Maybe; - languageCode?: Maybe; - sku?: Maybe; - name?: Maybe; - price?: Maybe; - currencyCode?: Maybe; - priceIncludesTax?: Maybe; - priceWithTax?: Maybe; -}; - -export type ProductVariantSortParameter = { - stockOnHand?: Maybe; - stockAllocated?: Maybe; - outOfStockThreshold?: Maybe; - id?: Maybe; - productId?: Maybe; - createdAt?: Maybe; - updatedAt?: Maybe; - sku?: Maybe; - name?: Maybe; - price?: Maybe; - priceWithTax?: Maybe; -}; - export type HistoryEntryFilterParameter = { isPublic?: Maybe; createdAt?: Maybe; diff --git a/packages/core/e2e/graphql/generated-e2e-admin-types.ts b/packages/core/e2e/graphql/generated-e2e-admin-types.ts index 2e8bfd1f97..de60e2c4f7 100644 --- a/packages/core/e2e/graphql/generated-e2e-admin-types.ts +++ b/packages/core/e2e/graphql/generated-e2e-admin-types.ts @@ -56,6 +56,8 @@ export type Query = { /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */ product?: Maybe; /** Get a ProductVariant by id */ + productVariants: ProductVariantList; + /** Get a ProductVariant by id */ productVariant?: Maybe; promotion?: Maybe; promotions: PromotionList; @@ -188,6 +190,10 @@ export type QueryProductArgs = { slug?: Maybe; }; +export type QueryProductVariantsArgs = { + options?: Maybe; +}; + export type QueryProductVariantArgs = { id: Scalars['ID']; }; @@ -3948,6 +3954,13 @@ export type ProductListOptions = { filter?: Maybe; }; +export type ProductVariantListOptions = { + skip?: Maybe; + take?: Maybe; + sort?: Maybe; + filter?: Maybe; +}; + export type PromotionListOptions = { skip?: Maybe; take?: Maybe; @@ -3976,13 +3989,6 @@ export type TaxRateListOptions = { filter?: Maybe; }; -export type ProductVariantListOptions = { - skip?: Maybe; - take?: Maybe; - sort?: Maybe; - filter?: Maybe; -}; - export type HistoryEntryListOptions = { skip?: Maybe; take?: Maybe; @@ -4209,6 +4215,38 @@ export type ProductSortParameter = { description?: Maybe; }; +export type ProductVariantFilterParameter = { + enabled?: Maybe; + trackInventory?: Maybe; + stockOnHand?: Maybe; + stockAllocated?: Maybe; + outOfStockThreshold?: Maybe; + useGlobalOutOfStockThreshold?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + languageCode?: Maybe; + sku?: Maybe; + name?: Maybe; + price?: Maybe; + currencyCode?: Maybe; + priceIncludesTax?: Maybe; + priceWithTax?: Maybe; +}; + +export type ProductVariantSortParameter = { + stockOnHand?: Maybe; + stockAllocated?: Maybe; + outOfStockThreshold?: Maybe; + id?: Maybe; + productId?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + sku?: Maybe; + name?: Maybe; + price?: Maybe; + priceWithTax?: Maybe; +}; + export type PromotionFilterParameter = { createdAt?: Maybe; updatedAt?: Maybe; @@ -4281,38 +4319,6 @@ export type TaxRateSortParameter = { value?: Maybe; }; -export type ProductVariantFilterParameter = { - enabled?: Maybe; - trackInventory?: Maybe; - stockOnHand?: Maybe; - stockAllocated?: Maybe; - outOfStockThreshold?: Maybe; - useGlobalOutOfStockThreshold?: Maybe; - createdAt?: Maybe; - updatedAt?: Maybe; - languageCode?: Maybe; - sku?: Maybe; - name?: Maybe; - price?: Maybe; - currencyCode?: Maybe; - priceIncludesTax?: Maybe; - priceWithTax?: Maybe; -}; - -export type ProductVariantSortParameter = { - stockOnHand?: Maybe; - stockAllocated?: Maybe; - outOfStockThreshold?: Maybe; - id?: Maybe; - productId?: Maybe; - createdAt?: Maybe; - updatedAt?: Maybe; - sku?: Maybe; - name?: Maybe; - price?: Maybe; - priceWithTax?: Maybe; -}; - export type HistoryEntryFilterParameter = { isPublic?: Maybe; createdAt?: Maybe; @@ -5946,6 +5952,16 @@ export type GetProductVariantQueryVariables = Exact<{ export type GetProductVariantQuery = { productVariant?: Maybe> }; +export type GetProductVariantListQueryVariables = Exact<{ + options?: Maybe; +}>; + +export type GetProductVariantListQuery = { + productVariants: Pick & { + items: Array>; + }; +}; + export type DeletePromotionMutationVariables = Exact<{ id: Scalars['ID']; }>; @@ -7970,6 +7986,15 @@ export namespace GetProductVariant { export type ProductVariant = NonNullable; } +export namespace GetProductVariantList { + export type Variables = GetProductVariantListQueryVariables; + export type Query = GetProductVariantListQuery; + export type ProductVariants = NonNullable; + export type Items = NonNullable< + NonNullable['items']>[number] + >; +} + export namespace DeletePromotion { export type Variables = DeletePromotionMutationVariables; export type Mutation = DeletePromotionMutation; diff --git a/packages/core/e2e/product.e2e-spec.ts b/packages/core/e2e/product.e2e-spec.ts index 29a237cbe0..89b5ef1ca0 100644 --- a/packages/core/e2e/product.e2e-spec.ts +++ b/packages/core/e2e/product.e2e-spec.ts @@ -372,41 +372,6 @@ describe('Product resolver', () => { }, ]); }); - - it('sort by price', async () => { - const { productVariants } = await adminClient.query< - GetProductVariantList.Query, - GetProductVariantList.Variables - >(GET_PRODUCT_VARIANT_LIST, { - options: { - take: 3, - sort: { - price: SortOrder.ASC, - }, - }, - }); - - expect(productVariants.items).toEqual([ - { - id: 'T_23', - name: 'Skipping Rope', - price: 799, - sku: 'B07CNGXVXT', - }, - { - id: 'T_20', - name: 'Tripod', - price: 1498, - sku: 'B00XI87KV8', - }, - { - id: 'T_32', - name: 'Spiky Cactus', - price: 1550, - sku: 'SC011001', - }, - ]); - }); }); describe('productVariant query', () => { diff --git a/packages/core/src/api/resolvers/admin/product.resolver.ts b/packages/core/src/api/resolvers/admin/product.resolver.ts index 315ebd5eac..ad5fef0409 100644 --- a/packages/core/src/api/resolvers/admin/product.resolver.ts +++ b/packages/core/src/api/resolvers/admin/product.resolver.ts @@ -17,6 +17,7 @@ import { QueryProductArgs, QueryProductsArgs, QueryProductVariantArgs, + QueryProductVariantsArgs, RemoveOptionGroupFromProductResult, } from '@vendure/common/lib/generated-types'; import { PaginatedList } from '@vendure/common/lib/shared-types'; @@ -70,6 +71,15 @@ export class ProductResolver { } } + @Query() + @Allow(Permission.ReadCatalog) + async productVariants( + @Ctx() ctx: RequestContext, + @Args() args: QueryProductVariantsArgs, + ): Promise>> { + return this.productVariantService.findAll(ctx, args.options || undefined); + } + @Query() @Allow(Permission.ReadCatalog) async productVariant( diff --git a/packages/core/src/api/schema/admin-api/product.api.graphql b/packages/core/src/api/schema/admin-api/product.api.graphql index 6ecadaa412..15640b0cc7 100644 --- a/packages/core/src/api/schema/admin-api/product.api.graphql +++ b/packages/core/src/api/schema/admin-api/product.api.graphql @@ -1,7 +1,10 @@ type Query { + "List Products" 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! "Get a ProductVariant by id" productVariant(id: ID!): ProductVariant } @@ -53,6 +56,9 @@ input StockMovementListOptions { # generated by generateListOptions function input ProductListOptions +# generated by generateListOptions function +input ProductVariantListOptions + input ProductTranslationInput { id: ID languageCode: LanguageCode! diff --git a/packages/core/src/api/schema/common/product.type.graphql b/packages/core/src/api/schema/common/product.type.graphql index 9dcf95717a..16005d1df0 100644 --- a/packages/core/src/api/schema/common/product.type.graphql +++ b/packages/core/src/api/schema/common/product.type.graphql @@ -30,6 +30,11 @@ type ProductList implements PaginatedList { totalItems: Int! } +type ProductVariantList implements PaginatedList { + items: [ProductVariant!]! + totalItems: Int! +} + type ProductVariant implements Node { id: ID! product: Product! diff --git a/packages/core/src/service/helpers/list-query-builder/parse-channel-param.ts b/packages/core/src/service/helpers/list-query-builder/parse-channel-param.ts index 3a79acbaa3..dc72b0526d 100644 --- a/packages/core/src/service/helpers/list-query-builder/parse-channel-param.ts +++ b/packages/core/src/service/helpers/list-query-builder/parse-channel-param.ts @@ -7,7 +7,7 @@ import { WhereCondition } from './parse-filter-params'; /** * Creates a WhereCondition for a channel-aware entity, filtering for only those entities - * which are assigned to the channel speicified by channelId, + * which are assigned to the channel specified by channelId, */ export function parseChannelParam( connection: Connection, diff --git a/packages/core/src/service/services/product-variant.service.ts b/packages/core/src/service/services/product-variant.service.ts index 04e2ed01fc..54e6b0fa4c 100644 --- a/packages/core/src/service/services/product-variant.service.ts +++ b/packages/core/src/service/services/product-variant.service.ts @@ -62,6 +62,30 @@ export class ProductVariantService { private roleService: RoleService, ) {} + async findAll( + ctx: RequestContext, + options?: ListQueryOptions, + ): Promise>> { + const relations = ['featuredAsset', 'taxCategory', 'channels']; + return this.listQueryBuilder + .build(ProductVariant, options, { + relations, + channelId: ctx.channelId, + where: { deletedAt: null }, + ctx, + }) + .getManyAndCount() + .then(async ([variants, totalItems]) => { + const items = variants.map(variant => + translateDeep(this.applyChannelPriceAndTax(variant, ctx), ctx.languageCode), + ); + return { + items, + totalItems, + }; + }); + } + findOne(ctx: RequestContext, productVariantId: ID): Promise | undefined> { const relations = ['product', 'product.featuredAsset', 'taxCategory']; return this.connection diff --git a/packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts b/packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts index be26da4715..40450baf90 100644 --- a/packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts +++ b/packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts @@ -56,6 +56,8 @@ export type Query = { /** Get a Product either by id or slug. If neither id nor slug is speicified, an error will result. */ product?: Maybe; /** Get a ProductVariant by id */ + productVariants: ProductVariantList; + /** Get a ProductVariant by id */ productVariant?: Maybe; promotion?: Maybe; promotions: PromotionList; @@ -188,6 +190,10 @@ export type QueryProductArgs = { slug?: Maybe; }; +export type QueryProductVariantsArgs = { + options?: Maybe; +}; + export type QueryProductVariantArgs = { id: Scalars['ID']; }; @@ -3948,6 +3954,13 @@ export type ProductListOptions = { filter?: Maybe; }; +export type ProductVariantListOptions = { + skip?: Maybe; + take?: Maybe; + sort?: Maybe; + filter?: Maybe; +}; + export type PromotionListOptions = { skip?: Maybe; take?: Maybe; @@ -3976,13 +3989,6 @@ export type TaxRateListOptions = { filter?: Maybe; }; -export type ProductVariantListOptions = { - skip?: Maybe; - take?: Maybe; - sort?: Maybe; - filter?: Maybe; -}; - export type HistoryEntryListOptions = { skip?: Maybe; take?: Maybe; @@ -4209,6 +4215,38 @@ export type ProductSortParameter = { description?: Maybe; }; +export type ProductVariantFilterParameter = { + enabled?: Maybe; + trackInventory?: Maybe; + stockOnHand?: Maybe; + stockAllocated?: Maybe; + outOfStockThreshold?: Maybe; + useGlobalOutOfStockThreshold?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + languageCode?: Maybe; + sku?: Maybe; + name?: Maybe; + price?: Maybe; + currencyCode?: Maybe; + priceIncludesTax?: Maybe; + priceWithTax?: Maybe; +}; + +export type ProductVariantSortParameter = { + stockOnHand?: Maybe; + stockAllocated?: Maybe; + outOfStockThreshold?: Maybe; + id?: Maybe; + productId?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + sku?: Maybe; + name?: Maybe; + price?: Maybe; + priceWithTax?: Maybe; +}; + export type PromotionFilterParameter = { createdAt?: Maybe; updatedAt?: Maybe; @@ -4281,38 +4319,6 @@ export type TaxRateSortParameter = { value?: Maybe; }; -export type ProductVariantFilterParameter = { - enabled?: Maybe; - trackInventory?: Maybe; - stockOnHand?: Maybe; - stockAllocated?: Maybe; - outOfStockThreshold?: Maybe; - useGlobalOutOfStockThreshold?: Maybe; - createdAt?: Maybe; - updatedAt?: Maybe; - languageCode?: Maybe; - sku?: Maybe; - name?: Maybe; - price?: Maybe; - currencyCode?: Maybe; - priceIncludesTax?: Maybe; - priceWithTax?: Maybe; -}; - -export type ProductVariantSortParameter = { - stockOnHand?: Maybe; - stockAllocated?: Maybe; - outOfStockThreshold?: Maybe; - id?: Maybe; - productId?: Maybe; - createdAt?: Maybe; - updatedAt?: Maybe; - sku?: Maybe; - name?: Maybe; - price?: Maybe; - priceWithTax?: Maybe; -}; - export type HistoryEntryFilterParameter = { isPublic?: Maybe; createdAt?: Maybe;