From 511f04d8abbfe9fe6fcb582f1a8731817b74f602 Mon Sep 17 00:00:00 2001 From: Michael Bromley Date: Mon, 4 Oct 2021 17:00:33 +0200 Subject: [PATCH] fix(core): Fix error when resolving deleted Product from Order Fixes #1125 --- ...generated-e2e-asset-server-plugin-types.ts | 7 -- packages/common/src/generated-shop-types.ts | 7 -- .../e2e/graphql/generated-e2e-admin-types.ts | 34 --------- .../e2e/graphql/generated-e2e-shop-types.ts | 76 +++++++++++++++++-- packages/core/e2e/graphql/shop-definitions.ts | 18 +++++ packages/core/e2e/order.e2e-spec.ts | 39 ++++++++++ .../connection/transactional-connection.ts | 4 +- packages/core/src/connection/types.ts | 9 +++ .../services/product-variant.service.ts | 4 +- ...enerated-e2e-elasticsearch-plugin-types.ts | 7 -- 10 files changed, 141 insertions(+), 64 deletions(-) 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 59b24f9529..fc1080c843 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 @@ -3351,10 +3351,7 @@ export type Product = Node & { description: Scalars['String']; featuredAsset?: Maybe; assets: Array; - /** Returns all ProductVariants */ variants: Array; - /** Returns a paginated, sortable, filterable list of ProductVariants */ - variantList: ProductVariantList; optionGroups: Array; facetValues: Array; translations: Array; @@ -3362,10 +3359,6 @@ export type Product = Node & { customFields?: Maybe; }; -export type ProductVariantListArgs = { - options?: Maybe; -}; - export type ProductFilterParameter = { enabled?: Maybe; createdAt?: Maybe; diff --git a/packages/common/src/generated-shop-types.ts b/packages/common/src/generated-shop-types.ts index 22b4f95fae..9b474c3a00 100644 --- a/packages/common/src/generated-shop-types.ts +++ b/packages/common/src/generated-shop-types.ts @@ -2303,10 +2303,7 @@ export type Product = Node & { description: Scalars['String']; featuredAsset?: Maybe; assets: Array; - /** Returns all ProductVariants */ variants: Array; - /** Returns a paginated, sortable, filterable list of ProductVariants */ - variantList: ProductVariantList; optionGroups: Array; facetValues: Array; translations: Array; @@ -2314,10 +2311,6 @@ export type Product = Node & { customFields?: Maybe; }; -export type ProductVariantListArgs = { - options?: Maybe; -}; - export type ProductFilterParameter = { createdAt?: Maybe; updatedAt?: Maybe; diff --git a/packages/core/e2e/graphql/generated-e2e-admin-types.ts b/packages/core/e2e/graphql/generated-e2e-admin-types.ts index 657f14b51c..2132845860 100644 --- a/packages/core/e2e/graphql/generated-e2e-admin-types.ts +++ b/packages/core/e2e/graphql/generated-e2e-admin-types.ts @@ -3351,10 +3351,7 @@ export type Product = Node & { description: Scalars['String']; featuredAsset?: Maybe; assets: Array; - /** Returns all ProductVariants */ variants: Array; - /** Returns a paginated, sortable, filterable list of ProductVariants */ - variantList: ProductVariantList; optionGroups: Array; facetValues: Array; translations: Array; @@ -3362,10 +3359,6 @@ export type Product = Node & { customFields?: Maybe; }; -export type ProductVariantListArgs = { - options?: Maybe; -}; - export type ProductFilterParameter = { enabled?: Maybe; createdAt?: Maybe; @@ -6509,19 +6502,6 @@ export type GetProductVariantListQuery = { }; }; -export type GetProductWithVariantListQueryVariables = Exact<{ - id?: Maybe; - variantListOptions?: Maybe; -}>; - -export type GetProductWithVariantListQuery = { - product?: Maybe< - Pick & { - variantList: Pick & { items: Array }; - } - >; -}; - export type DeletePromotionMutationVariables = Exact<{ id: Scalars['ID']; }>; @@ -8844,20 +8824,6 @@ export namespace GetProductVariantList { >; } -export namespace GetProductWithVariantList { - export type Variables = GetProductWithVariantListQueryVariables; - export type Query = GetProductWithVariantListQuery; - export type Product = NonNullable; - export type VariantList = NonNullable< - NonNullable['variantList'] - >; - export type Items = NonNullable< - NonNullable< - NonNullable['variantList']>['items'] - >[number] - >; -} - export namespace DeletePromotion { export type Variables = DeletePromotionMutationVariables; export type Mutation = DeletePromotionMutation; diff --git a/packages/core/e2e/graphql/generated-e2e-shop-types.ts b/packages/core/e2e/graphql/generated-e2e-shop-types.ts index 71e17b3c10..eedbd45f5a 100644 --- a/packages/core/e2e/graphql/generated-e2e-shop-types.ts +++ b/packages/core/e2e/graphql/generated-e2e-shop-types.ts @@ -2226,10 +2226,7 @@ export type Product = Node & { description: Scalars['String']; featuredAsset?: Maybe; assets: Array; - /** Returns all ProductVariants */ variants: Array; - /** Returns a paginated, sortable, filterable list of ProductVariants */ - variantList: ProductVariantList; optionGroups: Array; facetValues: Array; translations: Array; @@ -2237,10 +2234,6 @@ export type Product = Node & { customFields?: Maybe; }; -export type ProductVariantListArgs = { - options?: Maybe; -}; - export type ProductFilterParameter = { createdAt?: Maybe; updatedAt?: Maybe; @@ -3394,6 +3387,16 @@ export type GetProductStockLevelQuery = { product?: Maybe & { variants: Array> }>; }; +export type GetActiveCustomerWithOrdersProductSlugQueryVariables = Exact<{ + options?: Maybe; +}>; + +export type GetActiveCustomerWithOrdersProductSlugQuery = { + activeCustomer?: Maybe<{ + orders: { items: Array<{ lines: Array<{ productVariant: { product: Pick } }> }> }; + }>; +}; + type DiscriminateUnion = T extends U ? T : never; export namespace TestOrderFragment { @@ -3921,3 +3924,62 @@ export namespace GetProductStockLevel { NonNullable['variants']>[number] >; } + +export namespace GetActiveCustomerWithOrdersProductSlug { + export type Variables = GetActiveCustomerWithOrdersProductSlugQueryVariables; + export type Query = GetActiveCustomerWithOrdersProductSlugQuery; + export type ActiveCustomer = NonNullable; + export type Orders = NonNullable< + NonNullable['orders'] + >; + export type Items = NonNullable< + NonNullable< + NonNullable< + NonNullable['orders'] + >['items'] + >[number] + >; + export type Lines = NonNullable< + NonNullable< + NonNullable< + NonNullable< + NonNullable< + NonNullable['orders'] + >['items'] + >[number] + >['lines'] + >[number] + >; + export type ProductVariant = NonNullable< + NonNullable< + NonNullable< + NonNullable< + NonNullable< + NonNullable< + NonNullable< + GetActiveCustomerWithOrdersProductSlugQuery['activeCustomer'] + >['orders'] + >['items'] + >[number] + >['lines'] + >[number] + >['productVariant'] + >; + export type Product = NonNullable< + NonNullable< + NonNullable< + NonNullable< + NonNullable< + NonNullable< + NonNullable< + NonNullable< + GetActiveCustomerWithOrdersProductSlugQuery['activeCustomer'] + >['orders'] + >['items'] + >[number] + >['lines'] + >[number] + >['productVariant'] + >['product'] + >; +} diff --git a/packages/core/e2e/graphql/shop-definitions.ts b/packages/core/e2e/graphql/shop-definitions.ts index 3f73ae41db..7646d01e53 100644 --- a/packages/core/e2e/graphql/shop-definitions.ts +++ b/packages/core/e2e/graphql/shop-definitions.ts @@ -703,3 +703,21 @@ export const GET_PRODUCT_WITH_STOCK_LEVEL = gql` } } `; + +export const GET_ACTIVE_CUSTOMER_WITH_ORDERS_PRODUCT_SLUG = gql` + query GetActiveCustomerWithOrdersProductSlug($options: OrderListOptions) { + activeCustomer { + orders(options: $options) { + items { + lines { + productVariant { + product { + slug + } + } + } + } + } + } + } +`; diff --git a/packages/core/e2e/order.e2e-spec.ts b/packages/core/e2e/order.e2e-spec.ts index e452b68105..8493580313 100644 --- a/packages/core/e2e/order.e2e-spec.ts +++ b/packages/core/e2e/order.e2e-spec.ts @@ -35,6 +35,7 @@ import { CreateFulfillment, CreateShippingMethod, DeleteOrderNote, + DeleteProduct, DeleteShippingMethod, ErrorCode, FulfillmentFragment, @@ -69,6 +70,7 @@ import { AddPaymentToOrder, ApplyCouponCode, DeletionResult, + GetActiveCustomerWithOrdersProductSlug, GetActiveOrder, GetOrderByCodeWithPayments, SetShippingAddress, @@ -81,6 +83,7 @@ import { CANCEL_ORDER, CREATE_FULFILLMENT, CREATE_SHIPPING_METHOD, + DELETE_PRODUCT, DELETE_SHIPPING_METHOD, GET_CUSTOMER_LIST, GET_ORDER, @@ -97,6 +100,7 @@ import { ADD_ITEM_TO_ORDER, ADD_PAYMENT, APPLY_COUPON_CODE, + GET_ACTIVE_CUSTOMER_WITH_ORDERS_PRODUCT_SLUG, GET_ACTIVE_ORDER, GET_ORDER_BY_CODE_WITH_PAYMENTS, SET_SHIPPING_ADDRESS, @@ -2213,6 +2217,41 @@ describe('Orders resolver', () => { }); refundGuard.assertSuccess(refund2); }); + + // https://github.com/vendure-ecommerce/vendure/issues/1125 + it('resolves deleted Product of OrderLine ProductVariants', async () => { + await shopClient.asUserWithCredentials(customers[0].emailAddress, password); + const { addItemToOrder } = await shopClient.query< + AddItemToOrder.Mutation, + AddItemToOrder.Variables + >(ADD_ITEM_TO_ORDER, { + productVariantId: 'T_7', + quantity: 1, + }); + + await proceedToArrangingPayment(shopClient); + const order = await addPaymentToOrder(shopClient, singleStageRefundablePaymentMethod); + orderGuard.assertSuccess(order); + + await adminClient.query(DELETE_PRODUCT, { + id: 'T_3', + }); + + const { activeCustomer } = await shopClient.query< + GetActiveCustomerWithOrdersProductSlug.Query, + GetActiveCustomerWithOrdersProductSlug.Variables + >(GET_ACTIVE_CUSTOMER_WITH_ORDERS_PRODUCT_SLUG, { + options: { + sort: { + createdAt: SortOrder.ASC, + }, + }, + }); + expect( + activeCustomer!.orders.items[activeCustomer!.orders.items.length - 1].lines[0].productVariant + .product.slug, + ).toBe('gaming-pc'); + }); }); }); diff --git a/packages/core/src/connection/transactional-connection.ts b/packages/core/src/connection/transactional-connection.ts index 8ca766dc74..9acd781bc6 100644 --- a/packages/core/src/connection/transactional-connection.ts +++ b/packages/core/src/connection/transactional-connection.ts @@ -174,7 +174,9 @@ export class TransactionalConnection { } if ( !entity || - (entity.hasOwnProperty('deletedAt') && (entity as T & SoftDeletable).deletedAt !== null) + (entity.hasOwnProperty('deletedAt') && + (entity as T & SoftDeletable).deletedAt !== null && + options.includeSoftDeleted !== true) ) { throw new EntityNotFoundError(entityType.name as any, id); } diff --git a/packages/core/src/connection/types.ts b/packages/core/src/connection/types.ts index 09e1a3c030..76ddef3d65 100644 --- a/packages/core/src/connection/types.ts +++ b/packages/core/src/connection/types.ts @@ -31,4 +31,13 @@ export interface GetEntityOrThrowOptions extends FindOneOptions { * @default 25 */ retryDelay?: number; + /** + * @description + * If set to `true`, soft-deleted entities will be returned. Otherwise they will + * throw as if they did not exist. + * + * @since 1.3.0 + * @default false + */ + includeSoftDeleted?: boolean; } diff --git a/packages/core/src/service/services/product-variant.service.ts b/packages/core/src/service/services/product-variant.service.ts index ebd0323067..fd3e84b5f0 100644 --- a/packages/core/src/service/services/product-variant.service.ts +++ b/packages/core/src/service/services/product-variant.service.ts @@ -261,7 +261,9 @@ export class ProductVariantService { * page, this method returns on the Product itself. */ async getProductForVariant(ctx: RequestContext, variant: ProductVariant): Promise> { - const product = await this.connection.getEntityOrThrow(ctx, Product, variant.productId); + const product = await this.connection.getEntityOrThrow(ctx, Product, variant.productId, { + includeSoftDeleted: true, + }); return translateDeep(product, ctx.languageCode); } 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 f07b3ab51c..29927c7cb7 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 @@ -3351,10 +3351,7 @@ export type Product = Node & { description: Scalars['String']; featuredAsset?: Maybe; assets: Array; - /** Returns all ProductVariants */ variants: Array; - /** Returns a paginated, sortable, filterable list of ProductVariants */ - variantList: ProductVariantList; optionGroups: Array; facetValues: Array; translations: Array; @@ -3362,10 +3359,6 @@ export type Product = Node & { customFields?: Maybe; }; -export type ProductVariantListArgs = { - options?: Maybe; -}; - export type ProductFilterParameter = { enabled?: Maybe; createdAt?: Maybe;