Parcourir la source

fix(core): Fix error when resolving deleted Product from Order

Fixes #1125
Michael Bromley il y a 4 ans
Parent
commit
511f04d8ab

+ 0 - 7
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<Asset>;
     assets: Array<Asset>;
-    /** Returns all ProductVariants */
     variants: Array<ProductVariant>;
-    /** Returns a paginated, sortable, filterable list of ProductVariants */
-    variantList: ProductVariantList;
     optionGroups: Array<ProductOptionGroup>;
     facetValues: Array<FacetValue>;
     translations: Array<ProductTranslation>;
@@ -3362,10 +3359,6 @@ export type Product = Node & {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
-export type ProductVariantListArgs = {
-    options?: Maybe<ProductVariantListOptions>;
-};
-
 export type ProductFilterParameter = {
     enabled?: Maybe<BooleanOperators>;
     createdAt?: Maybe<DateOperators>;

+ 0 - 7
packages/common/src/generated-shop-types.ts

@@ -2303,10 +2303,7 @@ export type Product = Node & {
     description: Scalars['String'];
     featuredAsset?: Maybe<Asset>;
     assets: Array<Asset>;
-    /** Returns all ProductVariants */
     variants: Array<ProductVariant>;
-    /** Returns a paginated, sortable, filterable list of ProductVariants */
-    variantList: ProductVariantList;
     optionGroups: Array<ProductOptionGroup>;
     facetValues: Array<FacetValue>;
     translations: Array<ProductTranslation>;
@@ -2314,10 +2311,6 @@ export type Product = Node & {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
-export type ProductVariantListArgs = {
-    options?: Maybe<ProductVariantListOptions>;
-};
-
 export type ProductFilterParameter = {
     createdAt?: Maybe<DateOperators>;
     updatedAt?: Maybe<DateOperators>;

+ 0 - 34
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -3351,10 +3351,7 @@ export type Product = Node & {
     description: Scalars['String'];
     featuredAsset?: Maybe<Asset>;
     assets: Array<Asset>;
-    /** Returns all ProductVariants */
     variants: Array<ProductVariant>;
-    /** Returns a paginated, sortable, filterable list of ProductVariants */
-    variantList: ProductVariantList;
     optionGroups: Array<ProductOptionGroup>;
     facetValues: Array<FacetValue>;
     translations: Array<ProductTranslation>;
@@ -3362,10 +3359,6 @@ export type Product = Node & {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
-export type ProductVariantListArgs = {
-    options?: Maybe<ProductVariantListOptions>;
-};
-
 export type ProductFilterParameter = {
     enabled?: Maybe<BooleanOperators>;
     createdAt?: Maybe<DateOperators>;
@@ -6509,19 +6502,6 @@ export type GetProductVariantListQuery = {
     };
 };
 
-export type GetProductWithVariantListQueryVariables = Exact<{
-    id?: Maybe<Scalars['ID']>;
-    variantListOptions?: Maybe<ProductVariantListOptions>;
-}>;
-
-export type GetProductWithVariantListQuery = {
-    product?: Maybe<
-        Pick<Product, 'id'> & {
-            variantList: Pick<ProductVariantList, 'totalItems'> & { items: Array<ProductVariantFragment> };
-        }
-    >;
-};
-
 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<GetProductWithVariantListQuery['product']>;
-    export type VariantList = NonNullable<
-        NonNullable<GetProductWithVariantListQuery['product']>['variantList']
-    >;
-    export type Items = NonNullable<
-        NonNullable<
-            NonNullable<NonNullable<GetProductWithVariantListQuery['product']>['variantList']>['items']
-        >[number]
-    >;
-}
-
 export namespace DeletePromotion {
     export type Variables = DeletePromotionMutationVariables;
     export type Mutation = DeletePromotionMutation;

+ 69 - 7
packages/core/e2e/graphql/generated-e2e-shop-types.ts

@@ -2226,10 +2226,7 @@ export type Product = Node & {
     description: Scalars['String'];
     featuredAsset?: Maybe<Asset>;
     assets: Array<Asset>;
-    /** Returns all ProductVariants */
     variants: Array<ProductVariant>;
-    /** Returns a paginated, sortable, filterable list of ProductVariants */
-    variantList: ProductVariantList;
     optionGroups: Array<ProductOptionGroup>;
     facetValues: Array<FacetValue>;
     translations: Array<ProductTranslation>;
@@ -2237,10 +2234,6 @@ export type Product = Node & {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
-export type ProductVariantListArgs = {
-    options?: Maybe<ProductVariantListOptions>;
-};
-
 export type ProductFilterParameter = {
     createdAt?: Maybe<DateOperators>;
     updatedAt?: Maybe<DateOperators>;
@@ -3394,6 +3387,16 @@ export type GetProductStockLevelQuery = {
     product?: Maybe<Pick<Product, 'id'> & { variants: Array<Pick<ProductVariant, 'id' | 'stockLevel'>> }>;
 };
 
+export type GetActiveCustomerWithOrdersProductSlugQueryVariables = Exact<{
+    options?: Maybe<OrderListOptions>;
+}>;
+
+export type GetActiveCustomerWithOrdersProductSlugQuery = {
+    activeCustomer?: Maybe<{
+        orders: { items: Array<{ lines: Array<{ productVariant: { product: Pick<Product, 'slug'> } }> }> };
+    }>;
+};
+
 type DiscriminateUnion<T, U> = T extends U ? T : never;
 
 export namespace TestOrderFragment {
@@ -3921,3 +3924,62 @@ export namespace GetProductStockLevel {
         NonNullable<NonNullable<GetProductStockLevelQuery['product']>['variants']>[number]
     >;
 }
+
+export namespace GetActiveCustomerWithOrdersProductSlug {
+    export type Variables = GetActiveCustomerWithOrdersProductSlugQueryVariables;
+    export type Query = GetActiveCustomerWithOrdersProductSlugQuery;
+    export type ActiveCustomer = NonNullable<GetActiveCustomerWithOrdersProductSlugQuery['activeCustomer']>;
+    export type Orders = NonNullable<
+        NonNullable<GetActiveCustomerWithOrdersProductSlugQuery['activeCustomer']>['orders']
+    >;
+    export type Items = NonNullable<
+        NonNullable<
+            NonNullable<
+                NonNullable<GetActiveCustomerWithOrdersProductSlugQuery['activeCustomer']>['orders']
+            >['items']
+        >[number]
+    >;
+    export type Lines = NonNullable<
+        NonNullable<
+            NonNullable<
+                NonNullable<
+                    NonNullable<
+                        NonNullable<GetActiveCustomerWithOrdersProductSlugQuery['activeCustomer']>['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']
+    >;
+}

+ 18 - 0
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
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+`;

+ 39 - 0
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<DeleteProduct.Mutation, DeleteProduct.Variables>(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');
+        });
     });
 });
 

+ 3 - 1
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);
         }

+ 9 - 0
packages/core/src/connection/types.ts

@@ -31,4 +31,13 @@ export interface GetEntityOrThrowOptions<T = any> extends FindOneOptions<T> {
      * @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;
 }

+ 3 - 1
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<Translated<Product>> {
-        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);
     }
 

+ 0 - 7
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -3351,10 +3351,7 @@ export type Product = Node & {
     description: Scalars['String'];
     featuredAsset?: Maybe<Asset>;
     assets: Array<Asset>;
-    /** Returns all ProductVariants */
     variants: Array<ProductVariant>;
-    /** Returns a paginated, sortable, filterable list of ProductVariants */
-    variantList: ProductVariantList;
     optionGroups: Array<ProductOptionGroup>;
     facetValues: Array<FacetValue>;
     translations: Array<ProductTranslation>;
@@ -3362,10 +3359,6 @@ export type Product = Node & {
     customFields?: Maybe<Scalars['JSON']>;
 };
 
-export type ProductVariantListArgs = {
-    options?: Maybe<ProductVariantListOptions>;
-};
-
 export type ProductFilterParameter = {
     enabled?: Maybe<BooleanOperators>;
     createdAt?: Maybe<DateOperators>;