Răsfoiți Sursa

fix(core): Fix EntityHydrator missing nested relations

Fixes #1161
Michael Bromley 4 ani în urmă
părinte
comite
fbda3ddda2

+ 19 - 3
packages/core/e2e/entity-hydrator.e2e-spec.ts

@@ -1,5 +1,5 @@
 /* tslint:disable:no-non-null-assertion */
-import { mergeConfig, Product } from '@vendure/core';
+import { mergeConfig, Product, ProductVariant } from '@vendure/core';
 import { createTestEnvironment } from '@vendure/testing';
 import gql from 'graphql-tag';
 import path from 'path';
@@ -122,7 +122,7 @@ describe('Entity hydration', () => {
     // https://github.com/vendure-ecommerce/vendure/issues/1153
     it('correctly handles empty array relations', async () => {
         // Product T_5 has no asset defined
-        const { hydrateProductAsset } = await adminClient.query<HydrateProductAssetQuery>(
+        const { hydrateProductAsset } = await adminClient.query<{ hydrateProductAsset: Product }>(
             GET_HYDRATED_PRODUCT_ASSET,
             {
                 id: 'T_5',
@@ -131,6 +131,18 @@ describe('Entity hydration', () => {
 
         expect(hydrateProductAsset.assets).toEqual([]);
     });
+
+    // 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' },
+        );
+
+        expect(hydrateProductVariant.product.id).toBe('T_1');
+        expect(hydrateProductVariant.product.facetValues.map(fv => fv.id).sort()).toEqual(['T_1', 'T_2']);
+    });
 });
 
 function getVariantWithName(product: Product, name: string) {
@@ -138,7 +150,6 @@ function getVariantWithName(product: Product, name: string) {
 }
 
 type HydrateProductQuery = { hydrateProduct: Product };
-type HydrateProductAssetQuery = { hydrateProductAsset: Product };
 
 const GET_HYDRATED_PRODUCT = gql`
     query GetHydratedProduct($id: ID!) {
@@ -150,3 +161,8 @@ const GET_HYDRATED_PRODUCT_ASSET = gql`
         hydrateProductAsset(id: $id)
     }
 `;
+const GET_HYDRATED_VARIANT = gql`
+    query GetHydratedVariant($id: ID!) {
+        hydrateProductVariant(id: $id)
+    }
+`;

+ 17 - 1
packages/core/e2e/fixtures/test-plugins/hydration-test-plugin.ts

@@ -5,6 +5,7 @@ import {
     ID,
     PluginCommonModule,
     Product,
+    ProductVariantService,
     RequestContext,
     TransactionalConnection,
     VendurePlugin,
@@ -13,7 +14,11 @@ import gql from 'graphql-tag';
 
 @Resolver()
 export class TestAdminPluginResolver {
-    constructor(private connection: TransactionalConnection, private entityHydrator: EntityHydrator) {}
+    constructor(
+        private connection: TransactionalConnection,
+        private productVariantService: ProductVariantService,
+        private entityHydrator: EntityHydrator,
+    ) {}
 
     @Query()
     async hydrateProduct(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
@@ -45,6 +50,16 @@ export class TestAdminPluginResolver {
         });
         return product;
     }
+
+    // Test case for https://github.com/vendure-ecommerce/vendure/issues/1161
+    @Query()
+    async hydrateProductVariant(@Ctx() ctx: RequestContext, @Args() args: { id: ID }) {
+        const [variant] = await this.productVariantService.findByIds(ctx, [args.id]);
+        await this.entityHydrator.hydrate(ctx, variant, {
+            relations: ['product.facetValues.facet'],
+        });
+        return variant;
+    }
 }
 
 @VendurePlugin({
@@ -55,6 +70,7 @@ export class TestAdminPluginResolver {
             extend type Query {
                 hydrateProduct(id: ID!): JSON
                 hydrateProductAsset(id: ID!): JSON
+                hydrateProductVariant(id: ID!): JSON
             }
         `,
     },

+ 3 - 2
packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts

@@ -143,11 +143,11 @@ export class EntityHydrator {
         for (const relation of options.relations.slice().sort()) {
             if (typeof relation === 'string') {
                 const parts = relation.split('.');
-                let entity: any = target;
+                let entity: Record<string, any> | undefined = target;
                 const path = [];
                 for (const part of parts) {
                     path.push(part);
-                    if (entity[part]) {
+                    if (entity && entity[part]) {
                         entity = Array.isArray(entity[part]) ? entity[part][0] : entity[part];
                     } else {
                         const allParts = path.reduce((result, p, i) => {
@@ -158,6 +158,7 @@ export class EntityHydrator {
                             }
                         }, [] as string[]);
                         missingRelations.push(...allParts);
+                        entity = undefined;
                     }
                 }
             }