Procházet zdrojové kódy

fix(core): Fix edge case that breaks asset url prefixing

Michael Bromley před 3 měsíci
rodič
revize
7b3961362c

+ 177 - 1
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -6816,7 +6816,9 @@ export type UpdateGlobalLanguagesMutationVariables = Exact<{
 }>;
 
 export type UpdateGlobalLanguagesMutation = {
-    updateGlobalSettings: { id: string; availableLanguages: Array<LanguageCode> } | {};
+    updateGlobalSettings:
+        | { id: string; availableLanguages: Array<LanguageCode> }
+        | Record<PropertyKey, never>;
 };
 
 export type GetCollectionsWithAssetsQueryVariables = Exact<{ [key: string]: never }>;
@@ -11649,6 +11651,21 @@ export type GetTaxRateListQuery = {
     };
 };
 
+export type OrderAssetEdgeCaseQueryVariables = Exact<{
+    id: Scalars['ID']['input'];
+}>;
+
+export type OrderAssetEdgeCaseQuery = {
+    order?: { id: string; lines: Array<{ id: string; featuredAsset?: { preview: string } | null }> } | null;
+};
+
+export type OrderDetailFragment = {
+    id: string;
+    lines: Array<{ id: string; featuredAsset?: { preview: string } | null }>;
+};
+
+export type OrderLineFragment = { id: string; featuredAsset?: { preview: string } | null };
+
 export type GetOrderWithLineCalculatedPropsQueryVariables = Exact<{
     id: Scalars['ID']['input'];
 }>;
@@ -15339,6 +15356,75 @@ export const OrderWithModificationsFragmentDoc = {
         },
     ],
 } as unknown as DocumentNode<OrderWithModificationsFragment, unknown>;
+export const OrderLineFragmentDoc = {
+    kind: 'Document',
+    definitions: [
+        {
+            kind: 'FragmentDefinition',
+            name: { kind: 'Name', value: 'OrderLine' },
+            typeCondition: { kind: 'NamedType', name: { kind: 'Name', value: 'OrderLine' } },
+            selectionSet: {
+                kind: 'SelectionSet',
+                selections: [
+                    { kind: 'Field', name: { kind: 'Name', value: 'id' } },
+                    {
+                        kind: 'Field',
+                        name: { kind: 'Name', value: 'featuredAsset' },
+                        selectionSet: {
+                            kind: 'SelectionSet',
+                            selections: [{ kind: 'Field', name: { kind: 'Name', value: 'preview' } }],
+                        },
+                    },
+                ],
+            },
+        },
+    ],
+} as unknown as DocumentNode<OrderLineFragment, unknown>;
+export const OrderDetailFragmentDoc = {
+    kind: 'Document',
+    definitions: [
+        {
+            kind: 'FragmentDefinition',
+            name: { kind: 'Name', value: 'OrderDetail' },
+            typeCondition: { kind: 'NamedType', name: { kind: 'Name', value: 'Order' } },
+            selectionSet: {
+                kind: 'SelectionSet',
+                selections: [
+                    { kind: 'Field', name: { kind: 'Name', value: 'id' } },
+                    {
+                        kind: 'Field',
+                        name: { kind: 'Name', value: 'lines' },
+                        selectionSet: {
+                            kind: 'SelectionSet',
+                            selections: [
+                                { kind: 'FragmentSpread', name: { kind: 'Name', value: 'OrderLine' } },
+                            ],
+                        },
+                    },
+                ],
+            },
+        },
+        {
+            kind: 'FragmentDefinition',
+            name: { kind: 'Name', value: 'OrderLine' },
+            typeCondition: { kind: 'NamedType', name: { kind: 'Name', value: 'OrderLine' } },
+            selectionSet: {
+                kind: 'SelectionSet',
+                selections: [
+                    { kind: 'Field', name: { kind: 'Name', value: 'id' } },
+                    {
+                        kind: 'Field',
+                        name: { kind: 'Name', value: 'featuredAsset' },
+                        selectionSet: {
+                            kind: 'SelectionSet',
+                            selections: [{ kind: 'Field', name: { kind: 'Name', value: 'preview' } }],
+                        },
+                    },
+                ],
+            },
+        },
+    ],
+} as unknown as DocumentNode<OrderDetailFragment, unknown>;
 export const RefundFragmentDoc = {
     kind: 'Document',
     definitions: [
@@ -34905,6 +34991,96 @@ export const GetTaxRateListDocument = {
         },
     ],
 } as unknown as DocumentNode<GetTaxRateListQuery, GetTaxRateListQueryVariables>;
+export const OrderAssetEdgeCaseDocument = {
+    kind: 'Document',
+    definitions: [
+        {
+            kind: 'OperationDefinition',
+            operation: 'query',
+            name: { kind: 'Name', value: 'OrderAssetEdgeCase' },
+            variableDefinitions: [
+                {
+                    kind: 'VariableDefinition',
+                    variable: { kind: 'Variable', name: { kind: 'Name', value: 'id' } },
+                    type: {
+                        kind: 'NonNullType',
+                        type: { kind: 'NamedType', name: { kind: 'Name', value: 'ID' } },
+                    },
+                },
+            ],
+            selectionSet: {
+                kind: 'SelectionSet',
+                selections: [
+                    {
+                        kind: 'Field',
+                        name: { kind: 'Name', value: 'order' },
+                        arguments: [
+                            {
+                                kind: 'Argument',
+                                name: { kind: 'Name', value: 'id' },
+                                value: { kind: 'Variable', name: { kind: 'Name', value: 'id' } },
+                            },
+                        ],
+                        selectionSet: {
+                            kind: 'SelectionSet',
+                            selections: [
+                                {
+                                    kind: 'Field',
+                                    name: { kind: 'Name', value: 'lines' },
+                                    selectionSet: {
+                                        kind: 'SelectionSet',
+                                        selections: [{ kind: 'Field', name: { kind: 'Name', value: 'id' } }],
+                                    },
+                                },
+                                { kind: 'FragmentSpread', name: { kind: 'Name', value: 'OrderDetail' } },
+                            ],
+                        },
+                    },
+                ],
+            },
+        },
+        {
+            kind: 'FragmentDefinition',
+            name: { kind: 'Name', value: 'OrderLine' },
+            typeCondition: { kind: 'NamedType', name: { kind: 'Name', value: 'OrderLine' } },
+            selectionSet: {
+                kind: 'SelectionSet',
+                selections: [
+                    { kind: 'Field', name: { kind: 'Name', value: 'id' } },
+                    {
+                        kind: 'Field',
+                        name: { kind: 'Name', value: 'featuredAsset' },
+                        selectionSet: {
+                            kind: 'SelectionSet',
+                            selections: [{ kind: 'Field', name: { kind: 'Name', value: 'preview' } }],
+                        },
+                    },
+                ],
+            },
+        },
+        {
+            kind: 'FragmentDefinition',
+            name: { kind: 'Name', value: 'OrderDetail' },
+            typeCondition: { kind: 'NamedType', name: { kind: 'Name', value: 'Order' } },
+            selectionSet: {
+                kind: 'SelectionSet',
+                selections: [
+                    { kind: 'Field', name: { kind: 'Name', value: 'id' } },
+                    {
+                        kind: 'Field',
+                        name: { kind: 'Name', value: 'lines' },
+                        selectionSet: {
+                            kind: 'SelectionSet',
+                            selections: [
+                                { kind: 'FragmentSpread', name: { kind: 'Name', value: 'OrderLine' } },
+                            ],
+                        },
+                    },
+                ],
+            },
+        },
+    ],
+} as unknown as DocumentNode<OrderAssetEdgeCaseQuery, OrderAssetEdgeCaseQueryVariables>;
 export const GetOrderWithLineCalculatedPropsDocument = {
     kind: 'Document',
     definitions: [

+ 47 - 7
packages/core/e2e/order.e2e-spec.ts

@@ -257,8 +257,48 @@ describe('Orders resolver', () => {
             expect(result.order!.id).toBe('T_2');
         });
 
+        it('correctly resolves asset preview urls with edge-case query', async () => {
+            // This came up as a strange edge-case where the AssetInterceptorPlugin was unable to
+            // correctly transform the asset preview URL. It is not directly to do with Orders per se,
+            // but manifested when attempting an order-related query.
+            const result = await adminClient.query<Codegen.GetOrderQuery, Codegen.GetOrderQueryVariables>(
+                gql(`
+                    query OrderAssetEdgeCase($id: ID!) {
+                        order(id: $id) {
+                             lines {
+                               id
+                             }
+                            ...OrderDetail
+                        }
+                    }
+
+                    fragment OrderDetail on Order {
+                        id
+                        lines {
+                            ...OrderLine
+                        }
+                    }
+
+                    fragment OrderLine on OrderLine {
+                        id
+                        featuredAsset {
+                            preview
+                        }
+                    }
+                `),
+                {
+                    id: 'T_2',
+                },
+            );
+            expect(result.order!.lines.length).toBe(2);
+            expect(result.order!.lines.map(l => l.featuredAsset?.preview)).toEqual([
+                'test-url/test-assets/derick-david-409858-unsplash__preview.jpg',
+                'test-url/test-assets/derick-david-409858-unsplash__preview.jpg',
+            ]);
+        });
+
         it('order with calculated line properties', async () => {
-            const result = await adminClient.query<GetOrder.Query, GetOrder.Variables>(
+            const result = await adminClient.query<Codegen.GetOrderQuery, Codegen.GetOrderQueryVariables>(
                 gql`
                     query GetOrderWithLineCalculatedProps($id: ID!) {
                         order(id: $id) {
@@ -2844,19 +2884,19 @@ describe('Orders resolver', () => {
                     {
                         productVariantId: 'T_2',
                         quantity: 999999, // Exceeds limit
-                    }
+                    },
                 ],
             });
-            const t1 = addItemsToOrder.order.lines.find(l => l.productVariant.id === 'T_1')
+            const t1 = addItemsToOrder.order.lines.find(l => l.productVariant.id === 'T_1');
             // Should have added 1 of T_1
             expect(t1?.quantity).toBe(1);
             // Should not have added T_2
-            const t2 = addItemsToOrder.order.lines.find(l => l.productVariant.id === 'T_2')
-            expect(t2).toBeUndefined(); 
+            const t2 = addItemsToOrder.order.lines.find(l => l.productVariant.id === 'T_2');
+            expect(t2).toBeUndefined();
             // Should have errors
             expect(addItemsToOrder.errorResults.length).toBe(1);
-            expect(addItemsToOrder.errorResults[0].errorCode).toBe('ORDER_LIMIT_ERROR')
-            expect(addItemsToOrder.errorResults[0].message).toBe('ORDER_LIMIT_ERROR')
+            expect(addItemsToOrder.errorResults[0].errorCode).toBe('ORDER_LIMIT_ERROR');
+            expect(addItemsToOrder.errorResults[0].message).toBe('ORDER_LIMIT_ERROR');
         });
     });
 });

+ 8 - 0
packages/core/src/api/common/graphql-value-transformer.ts

@@ -251,6 +251,14 @@ export class GraphqlValueTransformer {
                             source[key].children,
                         );
                     }
+                    // Merge fragmentRefs from both nodes, avoiding duplicates
+                    if (source[key].fragmentRefs && source[key].fragmentRefs.length > 0) {
+                        const existingRefs = new Set(merged[key].fragmentRefs);
+                        const newRefs = source[key].fragmentRefs.filter(ref => !existingRefs.has(ref));
+                        if (newRefs.length > 0) {
+                            merged[key].fragmentRefs = [...merged[key].fragmentRefs, ...newRefs];
+                        }
+                    }
                 } else {
                     merged[key] = source[key];
                 }