diff --git a/.changeset/fix-visitor-consent-conditional.md b/.changeset/fix-visitor-consent-conditional.md new file mode 100644 index 0000000000..b0fa30d3d5 --- /dev/null +++ b/.changeset/fix-visitor-consent-conditional.md @@ -0,0 +1,10 @@ +--- +'@shopify/hydrogen': patch +'@shopify/hydrogen-react': patch +--- + +Fix cart operations failing on stores without `VisitorConsent` type + +Cart operations (like `cart.setMetafields()`) were unconditionally including the `visitorConsent` parameter in GraphQL operations, even when not being used. This caused failures on stores whose Storefront API schema doesn't include the `VisitorConsent` type (older API versions or certain store configurations). + +The `visitorConsent` parameter is now only included in cart GraphQL operations when explicitly provided. This restores compatibility with stores that don't support the `VisitorConsent` type while preserving the feature for users who need it. diff --git a/packages/hydrogen-react/src/cart-queries.test.ts b/packages/hydrogen-react/src/cart-queries.test.ts new file mode 100644 index 0000000000..b60f1422f9 --- /dev/null +++ b/packages/hydrogen-react/src/cart-queries.test.ts @@ -0,0 +1,56 @@ +import {describe, it, expect} from 'vitest'; +import { + CartLineAdd, + CartCreate, + CartLineRemove, + CartLineUpdate, + CartNoteUpdate, + CartBuyerIdentityUpdate, + CartAttributesUpdate, + CartDiscountCodesUpdate, + CartQuery, +} from './cart-queries'; + +const MOCK_FRAGMENT = '...MockFragment'; + +describe('cart-queries', () => { + describe('visitorConsent conditional inclusion', () => { + const queries = [ + {name: 'CartLineAdd', fn: CartLineAdd}, + {name: 'CartCreate', fn: CartCreate}, + {name: 'CartLineRemove', fn: CartLineRemove}, + {name: 'CartLineUpdate', fn: CartLineUpdate}, + {name: 'CartNoteUpdate', fn: CartNoteUpdate}, + {name: 'CartBuyerIdentityUpdate', fn: CartBuyerIdentityUpdate}, + {name: 'CartAttributesUpdate', fn: CartAttributesUpdate}, + {name: 'CartDiscountCodesUpdate', fn: CartDiscountCodesUpdate}, + {name: 'CartQuery', fn: CartQuery}, + ]; + + describe.each(queries)('$name', ({fn}) => { + it('should not include visitorConsent by default', () => { + const query = fn(MOCK_FRAGMENT); + + expect(query).toContain('@inContext'); + expect(query).not.toContain('visitorConsent'); + expect(query).not.toContain('VisitorConsent'); + }); + + it('should not include visitorConsent when explicitly disabled', () => { + const query = fn(MOCK_FRAGMENT, {includeVisitorConsent: false}); + + expect(query).toContain('@inContext'); + expect(query).not.toContain('visitorConsent'); + expect(query).not.toContain('VisitorConsent'); + }); + + it('should include visitorConsent when explicitly enabled', () => { + const query = fn(MOCK_FRAGMENT, {includeVisitorConsent: true}); + + expect(query).toContain('@inContext'); + expect(query).toContain('$visitorConsent: VisitorConsent'); + expect(query).toContain('visitorConsent: $visitorConsent'); + }); + }); + }); +}); diff --git a/packages/hydrogen-react/src/cart-queries.ts b/packages/hydrogen-react/src/cart-queries.ts index 531562349e..9039533111 100644 --- a/packages/hydrogen-react/src/cart-queries.ts +++ b/packages/hydrogen-react/src/cart-queries.ts @@ -1,17 +1,41 @@ -export const CartLineAdd = (cartFragment: string): string => /* GraphQL */ ` +type CartQueryOptions = { + includeVisitorConsent?: boolean; +}; + +function getInContextVariables(includeVisitorConsent: boolean): string { + const base = `$country: CountryCode = ZZ + $language: LanguageCode`; + + return includeVisitorConsent + ? `${base} + $visitorConsent: VisitorConsent` + : base; +} + +function getInContextDirective(includeVisitorConsent: boolean): string { + return includeVisitorConsent + ? `@inContext( + country: $country + language: $language + visitorConsent: $visitorConsent + )` + : `@inContext( + country: $country + language: $language + )`; +} + +export const CartLineAdd = ( + cartFragment: string, + options: CartQueryOptions = {}, +): string => /* GraphQL */ ` mutation CartLineAdd( $cartId: ID! $lines: [CartLineInput!]! $numCartLines: Int = 250 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent + ${getInContextVariables(options.includeVisitorConsent ?? false)} ) - @inContext( - country: $country - language: $language - visitorConsent: $visitorConsent - ) { + ${getInContextDirective(options.includeVisitorConsent ?? false)} { cartLinesAdd(cartId: $cartId, lines: $lines) { cart { ...CartFragment @@ -22,19 +46,16 @@ export const CartLineAdd = (cartFragment: string): string => /* GraphQL */ ` ${cartFragment} `; -export const CartCreate = (cartFragment: string): string => /* GraphQL */ ` +export const CartCreate = ( + cartFragment: string, + options: CartQueryOptions = {}, +): string => /* GraphQL */ ` mutation CartCreate( $input: CartInput! $numCartLines: Int = 250 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent + ${getInContextVariables(options.includeVisitorConsent ?? false)} ) - @inContext( - country: $country - language: $language - visitorConsent: $visitorConsent - ) { + ${getInContextDirective(options.includeVisitorConsent ?? false)} { cartCreate(input: $input) { cart { ...CartFragment @@ -45,20 +66,17 @@ export const CartCreate = (cartFragment: string): string => /* GraphQL */ ` ${cartFragment} `; -export const CartLineRemove = (cartFragment: string): string => /* GraphQL */ ` +export const CartLineRemove = ( + cartFragment: string, + options: CartQueryOptions = {}, +): string => /* GraphQL */ ` mutation CartLineRemove( $cartId: ID! $lines: [ID!]! $numCartLines: Int = 250 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent + ${getInContextVariables(options.includeVisitorConsent ?? false)} ) - @inContext( - country: $country - language: $language - visitorConsent: $visitorConsent - ) { + ${getInContextDirective(options.includeVisitorConsent ?? false)} { cartLinesRemove(cartId: $cartId, lineIds: $lines) { cart { ...CartFragment @@ -69,20 +87,17 @@ export const CartLineRemove = (cartFragment: string): string => /* GraphQL */ ` ${cartFragment} `; -export const CartLineUpdate = (cartFragment: string): string => /* GraphQL */ ` +export const CartLineUpdate = ( + cartFragment: string, + options: CartQueryOptions = {}, +): string => /* GraphQL */ ` mutation CartLineUpdate( $cartId: ID! $lines: [CartLineUpdateInput!]! $numCartLines: Int = 250 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent + ${getInContextVariables(options.includeVisitorConsent ?? false)} ) - @inContext( - country: $country - language: $language - visitorConsent: $visitorConsent - ) { + ${getInContextDirective(options.includeVisitorConsent ?? false)} { cartLinesUpdate(cartId: $cartId, lines: $lines) { cart { ...CartFragment @@ -93,20 +108,17 @@ export const CartLineUpdate = (cartFragment: string): string => /* GraphQL */ ` ${cartFragment} `; -export const CartNoteUpdate = (cartFragment: string): string => /* GraphQL */ ` +export const CartNoteUpdate = ( + cartFragment: string, + options: CartQueryOptions = {}, +): string => /* GraphQL */ ` mutation CartNoteUpdate( $cartId: ID! $note: String! $numCartLines: Int = 250 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent + ${getInContextVariables(options.includeVisitorConsent ?? false)} ) - @inContext( - country: $country - language: $language - visitorConsent: $visitorConsent - ) { + ${getInContextDirective(options.includeVisitorConsent ?? false)} { cartNoteUpdate(cartId: $cartId, note: $note) { cart { ...CartFragment @@ -119,20 +131,15 @@ export const CartNoteUpdate = (cartFragment: string): string => /* GraphQL */ ` export const CartBuyerIdentityUpdate = ( cartFragment: string, + options: CartQueryOptions = {}, ): string => /* GraphQL */ ` mutation CartBuyerIdentityUpdate( $cartId: ID! $buyerIdentity: CartBuyerIdentityInput! $numCartLines: Int = 250 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent + ${getInContextVariables(options.includeVisitorConsent ?? false)} ) - @inContext( - country: $country - language: $language - visitorConsent: $visitorConsent - ) { + ${getInContextDirective(options.includeVisitorConsent ?? false)} { cartBuyerIdentityUpdate(cartId: $cartId, buyerIdentity: $buyerIdentity) { cart { ...CartFragment @@ -145,20 +152,15 @@ export const CartBuyerIdentityUpdate = ( export const CartAttributesUpdate = ( cartFragment: string, + options: CartQueryOptions = {}, ): string => /* GraphQL */ ` mutation CartAttributesUpdate( $attributes: [AttributeInput!]! $cartId: ID! $numCartLines: Int = 250 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent + ${getInContextVariables(options.includeVisitorConsent ?? false)} ) - @inContext( - country: $country - language: $language - visitorConsent: $visitorConsent - ) { + ${getInContextDirective(options.includeVisitorConsent ?? false)} { cartAttributesUpdate(attributes: $attributes, cartId: $cartId) { cart { ...CartFragment @@ -171,20 +173,15 @@ export const CartAttributesUpdate = ( export const CartDiscountCodesUpdate = ( cartFragment: string, + options: CartQueryOptions = {}, ): string => /* GraphQL */ ` mutation CartDiscountCodesUpdate( $cartId: ID! $discountCodes: [String!]! $numCartLines: Int = 250 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent + ${getInContextVariables(options.includeVisitorConsent ?? false)} ) - @inContext( - country: $country - language: $language - visitorConsent: $visitorConsent - ) { + ${getInContextDirective(options.includeVisitorConsent ?? false)} { cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) { cart { ...CartFragment @@ -195,19 +192,16 @@ export const CartDiscountCodesUpdate = ( ${cartFragment} `; -export const CartQuery = (cartFragment: string): string => /* GraphQL */ ` +export const CartQuery = ( + cartFragment: string, + options: CartQueryOptions = {}, +): string => /* GraphQL */ ` query CartQuery( $id: ID! $numCartLines: Int = 250 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent + ${getInContextVariables(options.includeVisitorConsent ?? false)} ) - @inContext( - country: $country - language: $language - visitorConsent: $visitorConsent - ) { + ${getInContextDirective(options.includeVisitorConsent ?? false)} { cart(id: $id) { ...CartFragment } diff --git a/packages/hydrogen/src/cart/queries/cart-query-helpers.test.ts b/packages/hydrogen/src/cart/queries/cart-query-helpers.test.ts new file mode 100644 index 0000000000..8ea9f145c5 --- /dev/null +++ b/packages/hydrogen/src/cart/queries/cart-query-helpers.test.ts @@ -0,0 +1,81 @@ +import {describe, it, expect} from 'vitest'; +import { + getInContextVariables, + getInContextDirective, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; + +describe('cart-query-helpers', () => { + describe('getInContextVariables', () => { + it('returns base variables without visitorConsent when not included', () => { + const result = getInContextVariables(false); + + expect(result).toContain('$country: CountryCode = ZZ'); + expect(result).toContain('$language: LanguageCode'); + expect(result).not.toContain('visitorConsent'); + expect(result).not.toContain('VisitorConsent'); + }); + + it('defaults to excluding visitorConsent when called with no argument', () => { + const result = getInContextVariables(); + + expect(result).toContain('$country: CountryCode = ZZ'); + expect(result).not.toContain('visitorConsent'); + }); + + it('returns variables with visitorConsent when included', () => { + const result = getInContextVariables(true); + + expect(result).toContain('$country: CountryCode = ZZ'); + expect(result).toContain('$language: LanguageCode'); + expect(result).toContain('$visitorConsent: VisitorConsent'); + }); + }); + + describe('getInContextDirective', () => { + it('returns directive without visitorConsent when not included', () => { + const result = getInContextDirective(false); + + expect(result).toBe('@inContext(country: $country, language: $language)'); + expect(result).not.toContain('visitorConsent'); + }); + + it('defaults to excluding visitorConsent when called with no argument', () => { + const result = getInContextDirective(); + + expect(result).toBe('@inContext(country: $country, language: $language)'); + }); + + it('returns directive with visitorConsent when included', () => { + const result = getInContextDirective(true); + + expect(result).toBe( + '@inContext(country: $country, language: $language, visitorConsent: $visitorConsent)', + ); + }); + }); + + describe('shouldIncludeVisitorConsent', () => { + it('returns false when input is undefined', () => { + expect(shouldIncludeVisitorConsent(undefined)).toBe(false); + }); + + it('returns false when input has no visitorConsent key', () => { + expect(shouldIncludeVisitorConsent({})).toBe(false); + }); + + it('returns false when visitorConsent is explicitly undefined', () => { + expect(shouldIncludeVisitorConsent({visitorConsent: undefined})).toBe( + false, + ); + }); + + it('returns true when visitorConsent is provided', () => { + expect( + shouldIncludeVisitorConsent({ + visitorConsent: {saleOfData: 'SALE_OF_DATA_OPTED_OUT'}, + }), + ).toBe(true); + }); + }); +}); diff --git a/packages/hydrogen/src/cart/queries/cart-query-helpers.ts b/packages/hydrogen/src/cart/queries/cart-query-helpers.ts new file mode 100644 index 0000000000..6602ab83ea --- /dev/null +++ b/packages/hydrogen/src/cart/queries/cart-query-helpers.ts @@ -0,0 +1,45 @@ +/** + * Helper functions for building cart GraphQL operations with conditional + * @inContext directive support. + * + * The visitorConsent parameter is only included when explicitly provided, + * maintaining compatibility with stores whose API schema doesn't support it. + */ + +export type CartBuilderOptions = { + includeVisitorConsent?: boolean; +}; + +/** + * Returns true when the caller explicitly provides a visitorConsent value, + * indicating the store's Storefront API schema supports the VisitorConsent type. + */ +export function shouldIncludeVisitorConsent(input?: { + visitorConsent?: unknown; +}): boolean { + return input?.visitorConsent !== undefined; +} + +/** + * Builds the @inContext directive variable declarations for cart operations. + * Only includes visitorConsent when it's actually being used. + */ +export function getInContextVariables(includeVisitorConsent = false): string { + const base = `$country: CountryCode = ZZ + $language: LanguageCode`; + + return includeVisitorConsent + ? `${base} + $visitorConsent: VisitorConsent` + : base; +} + +/** + * Builds the @inContext directive for cart operations. + * Only includes visitorConsent when it's actually being used. + */ +export function getInContextDirective(includeVisitorConsent = false): string { + return includeVisitorConsent + ? '@inContext(country: $country, language: $language, visitorConsent: $visitorConsent)' + : '@inContext(country: $country, language: $language)'; +} diff --git a/packages/hydrogen/src/cart/queries/cartAttributesUpdateDefault.ts b/packages/hydrogen/src/cart/queries/cartAttributesUpdateDefault.ts index c40ebce1df..d943b1fa00 100644 --- a/packages/hydrogen/src/cart/queries/cartAttributesUpdateDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartAttributesUpdateDefault.ts @@ -11,6 +11,12 @@ import type { CartQueryOptions, } from './cart-types'; import type {AttributeInput} from '@shopify/hydrogen-react/storefront-api-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartAttributesUpdateFunction = ( attributes: AttributeInput[], @@ -21,30 +27,35 @@ export function cartAttributesUpdateDefault( options: CartQueryOptions, ): CartAttributesUpdateFunction { return async (attributes, optionalParams) => { + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartAttributesUpdate, errors} = await options.storefront.mutate<{ cartAttributesUpdate: CartQueryData; errors: StorefrontApiErrors; - }>(CART_ATTRIBUTES_UPDATE_MUTATION(options.cartFragment), { - variables: { - cartId: optionalParams?.cartId || options.getCartId(), - attributes, - ...optionalParams, + }>( + CART_ATTRIBUTES_UPDATE_MUTATION(options.cartFragment, { + includeVisitorConsent, + }), + { + variables: { + cartId: optionalParams?.cartId || options.getCartId(), + attributes, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartAttributesUpdate, errors); }; } export const CART_ATTRIBUTES_UPDATE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartAttributesUpdate( $cartId: ID! $attributes: [AttributeInput!]! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartAttributesUpdate(cartId: $cartId, attributes: $attributes) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartBuyerIdentityUpdateDefault.ts b/packages/hydrogen/src/cart/queries/cartBuyerIdentityUpdateDefault.ts index 2ebead1376..5b6b2167f0 100644 --- a/packages/hydrogen/src/cart/queries/cartBuyerIdentityUpdateDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartBuyerIdentityUpdateDefault.ts @@ -11,6 +11,12 @@ import type { CartQueryOptions, } from './cart-types'; import type {CartBuyerIdentityInput} from '@shopify/hydrogen-react/storefront-api-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartBuyerIdentityUpdateFunction = ( buyerIdentity: CartBuyerIdentityInput, @@ -31,19 +37,25 @@ export function cartBuyerIdentityUpdateDefault( ? await options.customerAccount.getBuyer() : undefined; + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartBuyerIdentityUpdate, errors} = await options.storefront.mutate<{ cartBuyerIdentityUpdate: CartQueryData; errors: StorefrontApiErrors; - }>(CART_BUYER_IDENTITY_UPDATE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - buyerIdentity: { - ...buyer, - ...buyerIdentity, + }>( + CART_BUYER_IDENTITY_UPDATE_MUTATION(options.cartFragment, { + includeVisitorConsent, + }), + { + variables: { + cartId: options.getCartId(), + buyerIdentity: { + ...buyer, + ...buyerIdentity, + }, + ...optionalParams, }, - ...optionalParams, }, - }); + ); return formatAPIResult(cartBuyerIdentityUpdate, errors); }; } @@ -51,14 +63,13 @@ export function cartBuyerIdentityUpdateDefault( //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartBuyerIdentityUpdate export const CART_BUYER_IDENTITY_UPDATE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartBuyerIdentityUpdate( $cartId: ID! $buyerIdentity: CartBuyerIdentityInput! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartBuyerIdentityUpdate(cartId: $cartId, buyerIdentity: $buyerIdentity) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartCreateDefault.ts b/packages/hydrogen/src/cart/queries/cartCreateDefault.ts index f7dd188c47..c295590fab 100644 --- a/packages/hydrogen/src/cart/queries/cartCreateDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartCreateDefault.ts @@ -11,6 +11,12 @@ import type { CartQueryDataReturn, } from './cart-types'; import type {CartInput} from '@shopify/hydrogen-react/storefront-api-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartCreateFunction = ( input: CartInput, @@ -26,10 +32,11 @@ export function cartCreateDefault( : undefined; const {cartId, ...restOfOptionalParams} = optionalParams || {}; const {buyerIdentity, ...restOfInput} = input; + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartCreate, errors} = await options.storefront.mutate<{ cartCreate: CartQueryData; errors: StorefrontApiErrors; - }>(CART_CREATE_MUTATION(options.cartFragment), { + }>(CART_CREATE_MUTATION(options.cartFragment, {includeVisitorConsent}), { variables: { input: { ...restOfInput, @@ -48,13 +55,12 @@ export function cartCreateDefault( //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartCreate export const CART_CREATE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartCreate( $input: CartInput! - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartCreate(input: $input) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartDeliveryAddressesAddDefault.tsx b/packages/hydrogen/src/cart/queries/cartDeliveryAddressesAddDefault.tsx index e6f74a848d..02c72d05be 100644 --- a/packages/hydrogen/src/cart/queries/cartDeliveryAddressesAddDefault.tsx +++ b/packages/hydrogen/src/cart/queries/cartDeliveryAddressesAddDefault.tsx @@ -11,6 +11,12 @@ import type { CartQueryDataReturn, CartQueryOptions, } from './cart-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartDeliveryAddressesAddFunction = ( addresses: Array, @@ -45,16 +51,22 @@ export function cartDeliveryAddressesAddDefault( addresses: Array, optionalParams, ) => { + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartDeliveryAddressesAdd, errors} = await options.storefront.mutate<{ cartDeliveryAddressesAdd: CartQueryData; errors: StorefrontApiErrors; - }>(CART_DELIVERY_ADDRESSES_ADD_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - addresses, - ...optionalParams, + }>( + CART_DELIVERY_ADDRESSES_ADD_MUTATION(options.cartFragment, { + includeVisitorConsent, + }), + { + variables: { + cartId: options.getCartId(), + addresses, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartDeliveryAddressesAdd, errors); }; @@ -63,14 +75,13 @@ export function cartDeliveryAddressesAddDefault( //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartDeliveryAddressesAdd export const CART_DELIVERY_ADDRESSES_ADD_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartDeliveryAddressesAdd( $cartId: ID! $addresses: [CartSelectableAddressInput!]!, - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartDeliveryAddressesAdd(addresses: $addresses, cartId: $cartId) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartDeliveryAddressesRemoveDefault.tsx b/packages/hydrogen/src/cart/queries/cartDeliveryAddressesRemoveDefault.tsx index baf70f7736..307eb73730 100644 --- a/packages/hydrogen/src/cart/queries/cartDeliveryAddressesRemoveDefault.tsx +++ b/packages/hydrogen/src/cart/queries/cartDeliveryAddressesRemoveDefault.tsx @@ -11,6 +11,12 @@ import type { CartQueryDataReturn, CartQueryOptions, } from './cart-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartDeliveryAddressesRemoveFunction = ( addressIds: Array | Array, @@ -40,17 +46,23 @@ export function cartDeliveryAddressesRemoveDefault( addressIds: Array | string[], optionalParams, ) => { + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartDeliveryAddressesRemove, errors} = await options.storefront.mutate<{ cartDeliveryAddressesRemove: CartQueryData; errors: StorefrontApiErrors; - }>(CART_DELIVERY_ADDRESSES_REMOVE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - addressIds, - ...optionalParams, + }>( + CART_DELIVERY_ADDRESSES_REMOVE_MUTATION(options.cartFragment, { + includeVisitorConsent, + }), + { + variables: { + cartId: options.getCartId(), + addressIds, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartDeliveryAddressesRemove, errors); }; @@ -59,14 +71,13 @@ export function cartDeliveryAddressesRemoveDefault( //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartDeliveryAddressesRemove export const CART_DELIVERY_ADDRESSES_REMOVE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartDeliveryAddressesRemove( $cartId: ID! $addressIds: [ID!]!, - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartDeliveryAddressesRemove(addressIds: $addressIds, cartId: $cartId) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartDeliveryAddressesUpdateDefault.tsx b/packages/hydrogen/src/cart/queries/cartDeliveryAddressesUpdateDefault.tsx index ee222e89a3..d735b1146a 100644 --- a/packages/hydrogen/src/cart/queries/cartDeliveryAddressesUpdateDefault.tsx +++ b/packages/hydrogen/src/cart/queries/cartDeliveryAddressesUpdateDefault.tsx @@ -11,6 +11,12 @@ import type { CartQueryDataReturn, CartQueryOptions, } from './cart-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartDeliveryAddressesUpdateFunction = ( addresses: Array, @@ -62,17 +68,23 @@ export function cartDeliveryAddressesUpdateDefault( addresses: Array, optionalParams, ) => { + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartDeliveryAddressesUpdate, errors} = await options.storefront.mutate<{ cartDeliveryAddressesUpdate: CartQueryData; errors: StorefrontApiErrors; - }>(CART_DELIVERY_ADDRESSES_UPDATE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - addresses, - ...optionalParams, + }>( + CART_DELIVERY_ADDRESSES_UPDATE_MUTATION(options.cartFragment, { + includeVisitorConsent, + }), + { + variables: { + cartId: options.getCartId(), + addresses, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartDeliveryAddressesUpdate, errors); }; @@ -81,14 +93,13 @@ export function cartDeliveryAddressesUpdateDefault( //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartDeliveryAddressesUpdate export const CART_DELIVERY_ADDRESSES_UPDATE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartDeliveryAddressesUpdate( $cartId: ID! $addresses: [CartSelectableAddressUpdateInput!]!, - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartDeliveryAddressesUpdate(addresses: $addresses, cartId: $cartId) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartDiscountCodesUpdateDefault.ts b/packages/hydrogen/src/cart/queries/cartDiscountCodesUpdateDefault.ts index 128b79477b..ac4f819a91 100644 --- a/packages/hydrogen/src/cart/queries/cartDiscountCodesUpdateDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartDiscountCodesUpdateDefault.ts @@ -10,6 +10,12 @@ import type { CartQueryDataReturn, CartQueryOptions, } from './cart-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartDiscountCodesUpdateFunction = ( discountCodes: string[], @@ -25,16 +31,22 @@ export function cartDiscountCodesUpdateDefault( return array.indexOf(value) === index; }); + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartDiscountCodesUpdate, errors} = await options.storefront.mutate<{ cartDiscountCodesUpdate: CartQueryData; errors: StorefrontApiErrors; - }>(CART_DISCOUNT_CODE_UPDATE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - discountCodes: uniqueCodes, - ...optionalParams, + }>( + CART_DISCOUNT_CODE_UPDATE_MUTATION(options.cartFragment, { + includeVisitorConsent, + }), + { + variables: { + cartId: options.getCartId(), + discountCodes: uniqueCodes, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartDiscountCodesUpdate, errors); }; } @@ -42,14 +54,13 @@ export function cartDiscountCodesUpdateDefault( //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartDiscountCodesUpdate export const CART_DISCOUNT_CODE_UPDATE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartDiscountCodesUpdate( $cartId: ID! $discountCodes: [String!]! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) { ... @defer { cart { diff --git a/packages/hydrogen/src/cart/queries/cartGetDefault.ts b/packages/hydrogen/src/cart/queries/cartGetDefault.ts index 71ddee722b..f5fb885b24 100644 --- a/packages/hydrogen/src/cart/queries/cartGetDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartGetDefault.ts @@ -7,6 +7,12 @@ import type { LanguageCode, VisitorConsent, } from '@shopify/hydrogen-react/storefront-api-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; type CartGetProps = { /** @@ -67,12 +73,16 @@ export function cartGetDefault({ if (!cartId) return null; + const includeVisitorConsent = shouldIncludeVisitorConsent(cartInput); const [isCustomerLoggedIn, {cart, errors}] = await Promise.all([ customerAccount ? customerAccount.isLoggedIn() : false, - storefront.query<{cart: Cart | null}>(CART_QUERY(cartFragment), { - variables: {cartId, ...cartInput}, - cache: storefront.CacheNone(), - }), + storefront.query<{cart: Cart | null}>( + CART_QUERY(cartFragment, {includeVisitorConsent}), + { + variables: {cartId, ...cartInput}, + cache: storefront.CacheNone(), + }, + ), ]); if (isCustomerLoggedIn && cart?.checkoutUrl) { @@ -86,14 +96,15 @@ export function cartGetDefault({ } //! @see https://shopify.dev/docs/api/storefront/latest/queries/cart -const CART_QUERY = (cartFragment = DEFAULT_CART_FRAGMENT) => `#graphql +const CART_QUERY = ( + cartFragment = DEFAULT_CART_FRAGMENT, + options: CartBuilderOptions = {}, +) => `#graphql query CartQuery( $cartId: ID! $numCartLines: Int = 100 - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cart(id: $cartId) { ...CartApiQuery } diff --git a/packages/hydrogen/src/cart/queries/cartGiftCardCodeUpdateDefault.ts b/packages/hydrogen/src/cart/queries/cartGiftCardCodeUpdateDefault.ts index 77db16f082..04568318b1 100644 --- a/packages/hydrogen/src/cart/queries/cartGiftCardCodeUpdateDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartGiftCardCodeUpdateDefault.ts @@ -10,6 +10,12 @@ import type { CartQueryDataReturn, CartQueryOptions, } from './cart-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartGiftCardCodesUpdateFunction = ( giftCardCodes: string[], @@ -32,16 +38,22 @@ export function cartGiftCardCodesUpdateDefault( options: CartQueryOptions, ): CartGiftCardCodesUpdateFunction { return async (giftCardCodes, optionalParams) => { + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartGiftCardCodesUpdate, errors} = await options.storefront.mutate<{ cartGiftCardCodesUpdate: CartQueryData; errors: StorefrontApiErrors; - }>(CART_GIFT_CARD_CODE_UPDATE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - giftCardCodes, - ...optionalParams, + }>( + CART_GIFT_CARD_CODE_UPDATE_MUTATION(options.cartFragment, { + includeVisitorConsent, + }), + { + variables: { + cartId: options.getCartId(), + giftCardCodes, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartGiftCardCodesUpdate, errors); }; } @@ -49,14 +61,13 @@ export function cartGiftCardCodesUpdateDefault( //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartGiftCardCodesUpdate export const CART_GIFT_CARD_CODE_UPDATE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartGiftCardCodesUpdate( $cartId: ID! $giftCardCodes: [String!]! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartGiftCardCodesUpdate(cartId: $cartId, giftCardCodes: $giftCardCodes) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartGiftCardCodesRemoveDefault.ts b/packages/hydrogen/src/cart/queries/cartGiftCardCodesRemoveDefault.ts index bd2c320f06..08d1259da1 100644 --- a/packages/hydrogen/src/cart/queries/cartGiftCardCodesRemoveDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartGiftCardCodesRemoveDefault.ts @@ -10,6 +10,12 @@ import type { CartQueryDataReturn, CartQueryOptions, } from './cart-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartGiftCardCodesRemoveFunction = ( appliedGiftCardIds: string[], @@ -20,16 +26,22 @@ export function cartGiftCardCodesRemoveDefault( options: CartQueryOptions, ): CartGiftCardCodesRemoveFunction { return async (appliedGiftCardIds, optionalParams) => { + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartGiftCardCodesRemove, errors} = await options.storefront.mutate<{ cartGiftCardCodesRemove: CartQueryData; errors: StorefrontApiErrors; - }>(CART_GIFT_CARD_CODES_REMOVE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - appliedGiftCardIds, - ...optionalParams, + }>( + CART_GIFT_CARD_CODES_REMOVE_MUTATION(options.cartFragment, { + includeVisitorConsent, + }), + { + variables: { + cartId: options.getCartId(), + appliedGiftCardIds, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartGiftCardCodesRemove, errors); }; } @@ -37,14 +49,13 @@ export function cartGiftCardCodesRemoveDefault( //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartGiftCardCodesRemove export const CART_GIFT_CARD_CODES_REMOVE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartGiftCardCodesRemove( $cartId: ID! $appliedGiftCardIds: [ID!]! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartGiftCardCodesRemove(cartId: $cartId, appliedGiftCardIds: $appliedGiftCardIds) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartLinesAddDefault.ts b/packages/hydrogen/src/cart/queries/cartLinesAddDefault.ts index f0b1435589..353c84436f 100644 --- a/packages/hydrogen/src/cart/queries/cartLinesAddDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartLinesAddDefault.ts @@ -11,6 +11,12 @@ import type { CartQueryDataReturn, CartQueryOptions, } from './cart-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartLinesAddFunction = ( lines: Array, @@ -21,10 +27,11 @@ export function cartLinesAddDefault( options: CartQueryOptions, ): CartLinesAddFunction { return async (lines, optionalParams) => { + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartLinesAdd, errors} = await options.storefront.mutate<{ cartLinesAdd: CartQueryData; errors: StorefrontApiErrors; - }>(CART_LINES_ADD_MUTATION(options.cartFragment), { + }>(CART_LINES_ADD_MUTATION(options.cartFragment, {includeVisitorConsent}), { variables: { cartId: options.getCartId(), lines, @@ -39,14 +46,13 @@ export function cartLinesAddDefault( //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesAdd export const CART_LINES_ADD_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartLinesAdd( $cartId: ID! $lines: [CartLineInput!]! - $country: CountryCode = ZZ - $language: LanguageCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartLinesAdd(cartId: $cartId, lines: $lines) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartLinesRemoveDefault.ts b/packages/hydrogen/src/cart/queries/cartLinesRemoveDefault.ts index 38b8cc38f1..584ab45bcb 100644 --- a/packages/hydrogen/src/cart/queries/cartLinesRemoveDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartLinesRemoveDefault.ts @@ -11,6 +11,12 @@ import type { CartQueryDataReturn, CartQueryOptions, } from './cart-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartLinesRemoveFunction = ( lineIds: string[], @@ -23,16 +29,20 @@ export function cartLinesRemoveDefault( return async (lineIds, optionalParams) => { throwIfLinesAreOptimistic('removeLines', lineIds); + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartLinesRemove, errors} = await options.storefront.mutate<{ cartLinesRemove: CartQueryData; errors: StorefrontApiErrors; - }>(CART_LINES_REMOVE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - lineIds, - ...optionalParams, + }>( + CART_LINES_REMOVE_MUTATION(options.cartFragment, {includeVisitorConsent}), + { + variables: { + cartId: options.getCartId(), + lineIds, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartLinesRemove, errors); }; } @@ -40,14 +50,13 @@ export function cartLinesRemoveDefault( //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesRemove export const CART_LINES_REMOVE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartLinesRemove( $cartId: ID! $lineIds: [ID!]! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartLinesRemove(cartId: $cartId, lineIds: $lineIds) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartLinesUpdateDefault.ts b/packages/hydrogen/src/cart/queries/cartLinesUpdateDefault.ts index 1f09da4c09..f37607e30a 100644 --- a/packages/hydrogen/src/cart/queries/cartLinesUpdateDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartLinesUpdateDefault.ts @@ -12,6 +12,12 @@ import type { CartQueryOptions, } from './cart-types'; import type {CartLineUpdateInput} from '@shopify/hydrogen-react/storefront-api-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartLinesUpdateFunction = ( lines: CartLineUpdateInput[], @@ -24,16 +30,20 @@ export function cartLinesUpdateDefault( return async (lines, optionalParams) => { throwIfLinesAreOptimistic('updateLines', lines); + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartLinesUpdate, errors} = await options.storefront.mutate<{ cartLinesUpdate: CartQueryData; errors: StorefrontApiErrors; - }>(CART_LINES_UPDATE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - lines, - ...optionalParams, + }>( + CART_LINES_UPDATE_MUTATION(options.cartFragment, {includeVisitorConsent}), + { + variables: { + cartId: options.getCartId(), + lines, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartLinesUpdate, errors); }; } @@ -41,14 +51,13 @@ export function cartLinesUpdateDefault( //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesUpdate export const CART_LINES_UPDATE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartLinesUpdate( $cartId: ID! $lines: [CartLineUpdateInput!]! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartLinesUpdate(cartId: $cartId, lines: $lines) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartMetafieldDeleteDefault.ts b/packages/hydrogen/src/cart/queries/cartMetafieldDeleteDefault.ts index 4cb56f7058..e905e98186 100644 --- a/packages/hydrogen/src/cart/queries/cartMetafieldDeleteDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartMetafieldDeleteDefault.ts @@ -9,6 +9,12 @@ import type { MetafieldDeleteUserError, Scalars, } from '@shopify/hydrogen-react/storefront-api-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartMetafieldDeleteFunction = ( key: Scalars['String']['input'], @@ -20,12 +26,13 @@ export function cartMetafieldDeleteDefault( ): CartMetafieldDeleteFunction { return async (key, optionalParams) => { const ownerId = optionalParams?.cartId || options.getCartId(); + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartMetafieldDelete, errors} = await options.storefront.mutate<{ cartMetafieldDelete: { userErrors: MetafieldDeleteUserError[]; }; errors: StorefrontApiErrors; - }>(CART_METAFIELD_DELETE_MUTATION(), { + }>(CART_METAFIELD_DELETE_MUTATION({includeVisitorConsent}), { variables: { input: { ownerId, @@ -47,13 +54,13 @@ export function cartMetafieldDeleteDefault( } //! @see https://shopify.dev/docs/api/storefront/2026-04/mutations/cartMetafieldDelete -export const CART_METAFIELD_DELETE_MUTATION = () => `#graphql +export const CART_METAFIELD_DELETE_MUTATION = ( + options: CartBuilderOptions = {}, +) => `#graphql mutation cartMetafieldDelete( $input: CartMetafieldDeleteInput! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartMetafieldDelete(input: $input) { userErrors { code diff --git a/packages/hydrogen/src/cart/queries/cartMetafieldsSetDefault.test.ts b/packages/hydrogen/src/cart/queries/cartMetafieldsSetDefault.test.ts index 0f0aeb3c2d..d18a83954e 100644 --- a/packages/hydrogen/src/cart/queries/cartMetafieldsSetDefault.test.ts +++ b/packages/hydrogen/src/cart/queries/cartMetafieldsSetDefault.test.ts @@ -1,6 +1,9 @@ import {describe, it, expect} from 'vitest'; import {CART_ID, mockCreateStorefrontClient} from '../cart-test-helper'; -import {cartMetafieldsSetDefault} from './cartMetafieldsSetDefault'; +import { + cartMetafieldsSetDefault, + CART_METAFIELD_SET_MUTATION, +} from './cartMetafieldsSetDefault'; describe('cartMetafieldsSetDefault', () => { it('should return a default cart metafields set implementation', async () => { @@ -27,4 +30,24 @@ describe('cartMetafieldsSetDefault', () => { expect(result.cart).toHaveProperty('id', CART_ID); expect(result.userErrors?.[0]).not.toContain(cartFragment); }); + + describe('mutation structure', () => { + it('should not include visitorConsent by default', () => { + const mutation = CART_METAFIELD_SET_MUTATION(); + + expect(mutation).toContain('@inContext'); + expect(mutation).not.toContain('visitorConsent'); + expect(mutation).not.toContain('VisitorConsent'); + }); + + it('should include visitorConsent when specified', () => { + const mutation = CART_METAFIELD_SET_MUTATION({ + includeVisitorConsent: true, + }); + + expect(mutation).toContain('@inContext'); + expect(mutation).toContain('$visitorConsent: VisitorConsent'); + expect(mutation).toContain('visitorConsent: $visitorConsent'); + }); + }); }); diff --git a/packages/hydrogen/src/cart/queries/cartMetafieldsSetDefault.ts b/packages/hydrogen/src/cart/queries/cartMetafieldsSetDefault.ts index e61431a2a2..a38f5905b2 100644 --- a/packages/hydrogen/src/cart/queries/cartMetafieldsSetDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartMetafieldsSetDefault.ts @@ -9,6 +9,12 @@ import type { Cart, MetafieldsSetUserError, } from '@shopify/hydrogen-react/storefront-api-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartMetafieldsSetFunction = ( metafields: MetafieldWithoutOwnerId[], @@ -26,12 +32,13 @@ export function cartMetafieldsSetDefault( ownerId, }), ); + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartMetafieldsSet, errors} = await options.storefront.mutate<{ cartMetafieldsSet: { userErrors: MetafieldsSetUserError[]; }; errors: StorefrontApiErrors; - }>(CART_METAFIELD_SET_MUTATION(), { + }>(CART_METAFIELD_SET_MUTATION({includeVisitorConsent}), { variables: {metafields: metafieldsWithOwnerId, ...optionalParams}, }); @@ -48,13 +55,13 @@ export function cartMetafieldsSetDefault( } //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartMetafieldsSet -export const CART_METAFIELD_SET_MUTATION = () => `#graphql +export const CART_METAFIELD_SET_MUTATION = ( + options: CartBuilderOptions = {}, +) => `#graphql mutation cartMetafieldsSet( $metafields: [CartMetafieldsSetInput!]! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartMetafieldsSet(metafields: $metafields) { userErrors { code diff --git a/packages/hydrogen/src/cart/queries/cartNoteUpdateDefault.ts b/packages/hydrogen/src/cart/queries/cartNoteUpdateDefault.ts index c4561448ab..689f11cc76 100644 --- a/packages/hydrogen/src/cart/queries/cartNoteUpdateDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartNoteUpdateDefault.ts @@ -10,6 +10,12 @@ import type { CartQueryDataReturn, CartQueryOptions, } from './cart-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartNoteUpdateFunction = ( note: string, @@ -20,16 +26,20 @@ export function cartNoteUpdateDefault( options: CartQueryOptions, ): CartNoteUpdateFunction { return async (note, optionalParams) => { + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartNoteUpdate, errors} = await options.storefront.mutate<{ cartNoteUpdate: CartQueryData; errors: StorefrontApiErrors; - }>(CART_NOTE_UPDATE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - note, - ...optionalParams, + }>( + CART_NOTE_UPDATE_MUTATION(options.cartFragment, {includeVisitorConsent}), + { + variables: { + cartId: options.getCartId(), + note, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartNoteUpdate, errors); }; } @@ -37,14 +47,13 @@ export function cartNoteUpdateDefault( //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartNoteUpdate export const CART_NOTE_UPDATE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartNoteUpdate( $cartId: ID! $note: String! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartNoteUpdate(cartId: $cartId, note: $note) { cart { ...CartApiMutation diff --git a/packages/hydrogen/src/cart/queries/cartSelectedDeliveryOptionsUpdateDefault.ts b/packages/hydrogen/src/cart/queries/cartSelectedDeliveryOptionsUpdateDefault.ts index fbb324e1d1..ed3314afc6 100644 --- a/packages/hydrogen/src/cart/queries/cartSelectedDeliveryOptionsUpdateDefault.ts +++ b/packages/hydrogen/src/cart/queries/cartSelectedDeliveryOptionsUpdateDefault.ts @@ -11,6 +11,12 @@ import type { CartQueryOptions, } from './cart-types'; import type {CartSelectedDeliveryOptionInput} from '@shopify/hydrogen-react/storefront-api-types'; +import { + getInContextVariables, + getInContextDirective, + CartBuilderOptions, + shouldIncludeVisitorConsent, +} from './cart-query-helpers'; export type CartSelectedDeliveryOptionsUpdateFunction = ( selectedDeliveryOptions: CartSelectedDeliveryOptionInput[], @@ -21,17 +27,23 @@ export function cartSelectedDeliveryOptionsUpdateDefault( options: CartQueryOptions, ): CartSelectedDeliveryOptionsUpdateFunction { return async (selectedDeliveryOptions, optionalParams) => { + const includeVisitorConsent = shouldIncludeVisitorConsent(optionalParams); const {cartSelectedDeliveryOptionsUpdate, errors} = await options.storefront.mutate<{ cartSelectedDeliveryOptionsUpdate: CartQueryData; errors: StorefrontApiErrors; - }>(CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION(options.cartFragment), { - variables: { - cartId: options.getCartId(), - selectedDeliveryOptions, - ...optionalParams, + }>( + CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION(options.cartFragment, { + includeVisitorConsent, + }), + { + variables: { + cartId: options.getCartId(), + selectedDeliveryOptions, + ...optionalParams, + }, }, - }); + ); return formatAPIResult(cartSelectedDeliveryOptionsUpdate, errors); }; } @@ -39,14 +51,13 @@ export function cartSelectedDeliveryOptionsUpdateDefault( //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartSelectedDeliveryOptionsUpdate export const CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION = ( cartFragment = MINIMAL_CART_FRAGMENT, + options: CartBuilderOptions = {}, ) => `#graphql mutation cartSelectedDeliveryOptionsUpdate( $cartId: ID! $selectedDeliveryOptions: [CartSelectedDeliveryOptionInput!]! - $language: LanguageCode - $country: CountryCode - $visitorConsent: VisitorConsent - ) @inContext(country: $country, language: $language, visitorConsent: $visitorConsent) { + ${getInContextVariables(options.includeVisitorConsent)} + ) ${getInContextDirective(options.includeVisitorConsent)} { cartSelectedDeliveryOptionsUpdate(cartId: $cartId, selectedDeliveryOptions: $selectedDeliveryOptions) { cart { ...CartApiMutation