Bläddra i källkod

fix(core): Fix EntityHydrator with entity getters

Fixes #1172
Michael Bromley 4 år sedan
förälder
incheckning
7d0e8943b8

+ 33 - 4
packages/core/e2e/entity-hydrator.e2e-spec.ts

@@ -1,6 +1,6 @@
 /* tslint:disable:no-non-null-assertion */
-import { mergeConfig, Product, ProductVariant } from '@vendure/core';
-import { createTestEnvironment } from '@vendure/testing';
+import { mergeConfig, Order, Product, ProductVariant } from '@vendure/core';
+import { createErrorResultGuard, createTestEnvironment, ErrorResultGuard } from '@vendure/testing';
 import gql from 'graphql-tag';
 import path from 'path';
 
@@ -8,9 +8,15 @@ import { initialData } from '../../../e2e-common/e2e-initial-data';
 import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
 
 import { HydrationTestPlugin } from './fixtures/test-plugins/hydration-test-plugin';
+import { AddItemToOrder, UpdatedOrderFragment } from './graphql/generated-e2e-shop-types';
+import { ADD_ITEM_TO_ORDER } from './graphql/shop-definitions';
+
+const orderResultGuard: ErrorResultGuard<UpdatedOrderFragment> = createErrorResultGuard(
+    input => !!input.lines,
+);
 
 describe('Entity hydration', () => {
-    const { server, adminClient } = createTestEnvironment(
+    const { server, adminClient, shopClient } = createTestEnvironment(
         mergeConfig(testConfig, {
             plugins: [HydrationTestPlugin],
         }),
@@ -134,7 +140,6 @@ describe('Entity hydration', () => {
 
     // https://github.com/vendure-ecommerce/vendure/issues/1161
     it('correctly expands missing relations', async () => {
-        // Product T_5 has no asset defined
         const { hydrateProductVariant } = await adminClient.query<{ hydrateProductVariant: ProductVariant }>(
             GET_HYDRATED_VARIANT,
             { id: 'T_1' },
@@ -143,6 +148,25 @@ describe('Entity hydration', () => {
         expect(hydrateProductVariant.product.id).toBe('T_1');
         expect(hydrateProductVariant.product.facetValues.map(fv => fv.id).sort()).toEqual(['T_1', 'T_2']);
     });
+
+    // https://github.com/vendure-ecommerce/vendure/issues/1172
+    it('can hydrate entity with getters (Order)', async () => {
+        const { addItemToOrder } = await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(
+            ADD_ITEM_TO_ORDER,
+            {
+                productVariantId: 'T_1',
+                quantity: 1,
+            },
+        );
+        orderResultGuard.assertSuccess(addItemToOrder);
+
+        const { hydrateOrder } = await adminClient.query<{ hydrateOrder: Order }>(GET_HYDRATED_ORDER, {
+            id: addItemToOrder.id,
+        });
+
+        expect(hydrateOrder.id).toBe('T_1');
+        expect(hydrateOrder.payments).toEqual([]);
+    });
 });
 
 function getVariantWithName(product: Product, name: string) {
@@ -166,3 +190,8 @@ const GET_HYDRATED_VARIANT = gql`
         hydrateProductVariant(id: $id)
     }
 `;
+const GET_HYDRATED_ORDER = gql`
+    query GetHydratedOrder($id: ID!) {
+        hydrateOrder(id: $id)
+    }
+`;

+ 14 - 0
packages/core/e2e/fixtures/test-plugins/hydration-test-plugin.ts

@@ -1,8 +1,10 @@
+/* tslint:disable:no-non-null-assertion */
 import { Args, Query, Resolver } from '@nestjs/graphql';
 import {
     Ctx,
     EntityHydrator,
     ID,
+    OrderService,
     PluginCommonModule,
     Product,
     ProductVariantService,
@@ -16,6 +18,7 @@ import gql from 'graphql-tag';
 export class TestAdminPluginResolver {
     constructor(
         private connection: TransactionalConnection,
+        private orderService: OrderService,
         private productVariantService: ProductVariantService,
         private entityHydrator: EntityHydrator,
     ) {}
@@ -60,6 +63,16 @@ export class TestAdminPluginResolver {
         });
         return variant;
     }
+
+    // Test case for https://github.com/vendure-ecommerce/vendure/issues/1172
+    @Query()
+    async hydrateOrder(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
+        const order = await this.orderService.findOne(ctx, args.id);
+        await this.entityHydrator.hydrate(ctx, order!, {
+            relations: ['payments'],
+        });
+        return order;
+    }
 }
 
 @VendurePlugin({
@@ -71,6 +84,7 @@ export class TestAdminPluginResolver {
                 hydrateProduct(id: ID!): JSON
                 hydrateProductAsset(id: ID!): JSON
                 hydrateProductVariant(id: ID!): JSON
+                hydrateOrder(id: ID!): JSON
             }
         `,
     },

+ 14 - 1
packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts

@@ -122,7 +122,7 @@ export class EntityHydrator {
                     .filter(item => this.isTranslatable(item.entity))
                     .map(item => item.relation.split('.'));
 
-                Object.assign(
+                this.assignSettableProperties(
                     target,
                     translateDeep(target as any, ctx.languageCode, translateDeepRelations as any),
                 );
@@ -131,6 +131,19 @@ export class EntityHydrator {
         return target;
     }
 
+    private assignSettableProperties<Entity extends VendureEntity>(target: Entity, source: Entity) {
+        for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(target))) {
+            if (typeof descriptor.get === 'function' && typeof descriptor.set !== 'function') {
+                // If the entity property has a getter only, we will skip it otherwise
+                // we will get an error of the form:
+                // `Cannot set property <name> of #<Entity> which has only a getter`
+                continue;
+            }
+            target[key as keyof Entity] = source[key as keyof Entity];
+        }
+        return target;
+    }
+
     /**
      * Compares the requested relations against the actual existing relations on the target entity,
      * and returns an array of all missing relation paths that would need to be fetched.