diff --git a/packages/commerce-sdk-react/src/hooks/ShopperBaskets/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperBaskets/query.ts index 5ad254d6cd..f254f4b330 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperBaskets/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperBaskets/query.ts @@ -5,23 +5,25 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperBaskets'] /** * Gets a basket. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Baskets `getBasket` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=getBasket| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperbaskets.shopperbaskets-1.html#getbasket | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useBasket = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -30,15 +32,16 @@ export const useBasket = ( const methodName = 'getBasket' const requiredParameters = ['organizationId', 'basketId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -46,13 +49,15 @@ export const useBasket = ( } /** * Gets applicable payment methods for an existing basket considering the open payment amount only. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Baskets `getPaymentMethodsForBasket` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=getPaymentMethodsForBasket| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperbaskets.shopperbaskets-1.html#getpaymentmethodsforbasket | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const usePaymentMethodsForBasket = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -61,15 +66,16 @@ export const usePaymentMethodsForBasket = ( const methodName = 'getPaymentMethodsForBasket' const requiredParameters = ['organizationId', 'basketId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -77,13 +83,15 @@ export const usePaymentMethodsForBasket = ( } /** * Gets applicable price books for an existing basket. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Baskets `getPriceBooksForBasket` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=getPriceBooksForBasket| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperbaskets.shopperbaskets-1.html#getpricebooksforbasket | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const usePriceBooksForBasket = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -92,15 +100,16 @@ export const usePriceBooksForBasket = ( const methodName = 'getPriceBooksForBasket' const requiredParameters = ['organizationId', 'basketId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -108,13 +117,15 @@ export const usePriceBooksForBasket = ( } /** * Gets the applicable shipping methods for a certain shipment of a basket. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Baskets `getShippingMethodsForShipment` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=getShippingMethodsForShipment| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperbaskets.shopperbaskets-1.html#getshippingmethodsforshipment | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useShippingMethodsForShipment = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -123,15 +134,16 @@ export const useShippingMethodsForShipment = ( const methodName = 'getShippingMethodsForShipment' const requiredParameters = ['organizationId', 'basketId', 'shipmentId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -139,13 +151,15 @@ export const useShippingMethodsForShipment = ( } /** * This method gives you the external taxation data set by the PUT taxes API. This endpoint can be called only if external taxation mode was used for basket creation. See POST /baskets for more information. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Baskets `getTaxesFromBasket` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=getTaxesFromBasket| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperbaskets.shopperbaskets-1.html#gettaxesfrombasket | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useTaxesFromBasket = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -154,15 +168,16 @@ export const useTaxesFromBasket = ( const methodName = 'getTaxesFromBasket' const requiredParameters = ['organizationId', 'basketId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters diff --git a/packages/commerce-sdk-react/src/hooks/ShopperBaskets/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperBaskets/queryKeyHelpers.ts index 8f75b0a606..9a4d50f3c4 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperBaskets/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperBaskets/queryKeyHelpers.ts @@ -10,40 +10,46 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperBaskets<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { - getBasket: ['/organizations/', string, '/baskets/', string, Params<'getBasket'>] + getBasket: [ + '/organizations/', + string | undefined, + '/baskets/', + string | undefined, + Params<'getBasket'> + ] getPaymentMethodsForBasket: [ '/organizations/', - string, + string | undefined, '/baskets/', - string, + string | undefined, '/payment-methods', Params<'getPaymentMethodsForBasket'> ] getPriceBooksForBasket: [ '/organizations/', - string, + string | undefined, '/baskets/', - string, + string | undefined, '/price-books', Params<'getPriceBooksForBasket'> ] getShippingMethodsForShipment: [ '/organizations/', - string, + string | undefined, '/baskets/', - string, + string | undefined, '/shipments/', - string, + string | undefined, '/shipping-methods', Params<'getShippingMethodsForShipment'> ] getTaxesFromBasket: [ '/organizations/', - string, + string | undefined, '/baskets/', - string, + string | undefined, '/taxes', Params<'getTaxesFromBasket'> ] diff --git a/packages/commerce-sdk-react/src/hooks/ShopperContexts/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperContexts/query.ts index a125569d79..b5958801fa 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperContexts/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperContexts/query.ts @@ -5,23 +5,25 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperContexts'] /** * Gets the shopper's context based on the shopperJWT. ******** This API is currently a work in progress, and not available to use yet. ******** + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Contexts `getShopperContext` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-contexts?meta=getShopperContext| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercontexts.shoppercontexts-1.html#getshoppercontext | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useShopperContext = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -30,15 +32,16 @@ export const useShopperContext = ( const methodName = 'getShopperContext' const requiredParameters = ['organizationId', 'usid'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters diff --git a/packages/commerce-sdk-react/src/hooks/ShopperContexts/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperContexts/queryKeyHelpers.ts index 4ed126e213..cb944a36e9 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperContexts/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperContexts/queryKeyHelpers.ts @@ -10,13 +10,13 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperContexts<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { getShopperContext: [ '/organizations/', - string, + string | undefined, '/shopper-context/', - string, + string | undefined, Params<'getShopperContext'> ] } diff --git a/packages/commerce-sdk-react/src/hooks/ShopperCustomers/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperCustomers/query.ts index 8e69565b39..13d36b7fc9 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperCustomers/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperCustomers/query.ts @@ -5,10 +5,10 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperCustomers'] @@ -16,13 +16,15 @@ type Client = ApiClients['shopperCustomers'] // TODO: Re-implement (and update description from RAML spec) when the endpoint exits closed beta. // /** // * Gets the new external profile for a customer.This endpoint is in closed beta, available to select few customers. Please get in touch with your Account Team if you'd like to participate in the beta program +// * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. +// * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. // * @returns A TanStack Query query hook with data from the Shopper Customers `getExternalProfile` endpoint. // * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getExternalProfile| Salesforce Developer Center} for more information about the API endpoint. // * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getexternalprofile | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. // * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. // */ // export const useExternalProfile = ( -// apiOptions: Argument, +// apiOptions: NullableParameters>, // queryOptions: ApiQueryOptions = {} // ): UseQueryResult> => { // type Options = Argument @@ -36,15 +38,16 @@ type Client = ApiClients['shopperCustomers'] // 'siteId' // ] as const -// // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order -// // to generate the correct query key. -// const netOptions = mergeOptions(client, apiOptions) +// // Parameters can be set in `apiOptions` or `client.clientConfig`; +// // we must merge them in order to generate the correct query key. +// const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) // const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) +// // We don't use `netOptions` here because we manipulate the options in `useQuery`. // const method = async (options: Options) => await client[methodName](options) // // For some reason, if we don't explicitly set these generic parameters, the inferred type for // // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. -// return useQuery(netOptions, queryOptions, { +// return useQuery(netOptions, queryOptions, { // method, // queryKey, // requiredParameters @@ -52,13 +55,15 @@ type Client = ApiClients['shopperCustomers'] // } /** * Gets a customer with all existing addresses and payment instruments associated with the requested customer. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getCustomer` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getCustomer| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getcustomer | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCustomer = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -67,15 +72,16 @@ export const useCustomer = ( const methodName = 'getCustomer' const requiredParameters = ['organizationId', 'customerId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -83,13 +89,15 @@ export const useCustomer = ( } /** * Retrieves a customer's address by address name. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getCustomerAddress` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getCustomerAddress| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getcustomeraddress | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCustomerAddress = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -98,15 +106,16 @@ export const useCustomerAddress = ( const methodName = 'getCustomerAddress' const requiredParameters = ['organizationId', 'customerId', 'addressName', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -114,13 +123,15 @@ export const useCustomerAddress = ( } /** * Gets the baskets of a customer. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getCustomerBaskets` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getCustomerBaskets| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getcustomerbaskets | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCustomerBaskets = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -129,15 +140,16 @@ export const useCustomerBaskets = ( const methodName = 'getCustomerBaskets' const requiredParameters = ['organizationId', 'customerId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -145,13 +157,15 @@ export const useCustomerBaskets = ( } /** * Returns a pageable list of all customer's orders. The default page size is 10. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getCustomerOrders` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getCustomerOrders| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getcustomerorders | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCustomerOrders = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -160,15 +174,16 @@ export const useCustomerOrders = ( const methodName = 'getCustomerOrders' const requiredParameters = ['organizationId', 'customerId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -176,13 +191,15 @@ export const useCustomerOrders = ( } /** * Retrieves a customer's payment instrument by its ID. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getCustomerPaymentInstrument` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getCustomerPaymentInstrument| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getcustomerpaymentinstrument | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCustomerPaymentInstrument = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -196,15 +213,16 @@ export const useCustomerPaymentInstrument = ( 'siteId' ] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -212,13 +230,15 @@ export const useCustomerPaymentInstrument = ( } /** * Returns all customer product lists. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getCustomerProductLists` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getCustomerProductLists| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getcustomerproductlists | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCustomerProductLists = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -227,15 +247,16 @@ export const useCustomerProductLists = ( const methodName = 'getCustomerProductLists' const requiredParameters = ['organizationId', 'customerId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -243,13 +264,15 @@ export const useCustomerProductLists = ( } /** * Returns a customer product list of the given customer and the items in the list. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getCustomerProductList` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getCustomerProductList| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getcustomerproductlist | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCustomerProductList = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -258,15 +281,16 @@ export const useCustomerProductList = ( const methodName = 'getCustomerProductList' const requiredParameters = ['organizationId', 'customerId', 'listId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -274,13 +298,15 @@ export const useCustomerProductList = ( } /** * Returns an item of a customer product list and the actual product details like image, availability and price. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getCustomerProductListItem` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getCustomerProductListItem| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getcustomerproductlistitem | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCustomerProductListItem = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -295,15 +321,16 @@ export const useCustomerProductListItem = ( 'siteId' ] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -311,13 +338,15 @@ export const useCustomerProductListItem = ( } /** * Retrieves all public product lists as defined by the given search term (for example, email OR first name and last name). + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getPublicProductListsBySearchTerm` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getPublicProductListsBySearchTerm| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getpublicproductlistsbysearchterm | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const usePublicProductListsBySearchTerm = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -326,15 +355,16 @@ export const usePublicProductListsBySearchTerm = ( const methodName = 'getPublicProductListsBySearchTerm' const requiredParameters = ['organizationId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -342,13 +372,15 @@ export const usePublicProductListsBySearchTerm = ( } /** * Retrieves a public product list by ID and the items under that product list. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getPublicProductList` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getPublicProductList| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getpublicproductlist | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const usePublicProductList = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -357,15 +389,16 @@ export const usePublicProductList = ( const methodName = 'getPublicProductList' const requiredParameters = ['organizationId', 'listId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -373,13 +406,15 @@ export const usePublicProductList = ( } /** * Retrieves an item from a public product list and the actual product details like product, image, availability and price. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Customers `getProductListItem` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-customers?meta=getProductListItem| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppercustomers.shoppercustomers-1.html#getproductlistitem | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useProductListItem = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -388,15 +423,16 @@ export const useProductListItem = ( const methodName = 'getProductListItem' const requiredParameters = ['organizationId', 'listId', 'itemId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters diff --git a/packages/commerce-sdk-react/src/hooks/ShopperCustomers/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperCustomers/queryKeyHelpers.ts index faa8df6222..96839905d3 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperCustomers/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperCustomers/queryKeyHelpers.ts @@ -10,97 +10,103 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperCustomers<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { getExternalProfile: [ '/organizations/', - string, + string | undefined, '/customers/external-profile', Params<'getExternalProfile'> ] - getCustomer: ['/organizations/', string, '/customers/', string, Params<'getCustomer'>] + getCustomer: [ + '/organizations/', + string | undefined, + '/customers/', + string | undefined, + Params<'getCustomer'> + ] getCustomerAddress: [ '/organizations/', - string, + string | undefined, '/customers/', - string, + string | undefined, '/addresses/', - string, + string | undefined, Params<'getCustomerAddress'> ] getCustomerBaskets: [ '/organizations/', - string, + string | undefined, '/customers/', - string, + string | undefined, '/baskets', Params<'getCustomerBaskets'> ] getCustomerOrders: [ '/organizations/', - string, + string | undefined, '/customers/', - string, + string | undefined, '/orders', Params<'getCustomerOrders'> ] getCustomerPaymentInstrument: [ '/organizations/', - string, + string | undefined, '/customers/', - string, + string | undefined, '/payment-instruments/', - string, + string | undefined, Params<'getCustomerPaymentInstrument'> ] getCustomerProductLists: [ '/organizations/', - string, + string | undefined, '/customers/', - string, + string | undefined, '/product-lists', Params<'getCustomerProductLists'> ] getCustomerProductList: [ '/organizations/', - string, + string | undefined, '/customers/', - string, + string | undefined, '/product-lists/', - string, + string | undefined, Params<'getCustomerProductList'> ] getCustomerProductListItem: [ '/organizations/', - string, + string | undefined, '/customers/', - string, + string | undefined, '/product-lists/', - string, + string | undefined, '/items/', - string, + string | undefined, Params<'getCustomerProductListItem'> ] getPublicProductListsBySearchTerm: [ '/organizations/', - string, + string | undefined, '/product-lists', Params<'getPublicProductListsBySearchTerm'> ] getPublicProductList: [ '/organizations/', - string, + string | undefined, '/product-lists/', - string, + string | undefined, Params<'getPublicProductList'> ] getProductListItem: [ '/organizations/', - string, + string | undefined, '/product-lists/', - string, + string | undefined, '/items/', - string, + string | undefined, Params<'getProductListItem'> ] } diff --git a/packages/commerce-sdk-react/src/hooks/ShopperExperience/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperExperience/query.ts index 81ff1a903f..1f94c93279 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperExperience/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperExperience/query.ts @@ -5,10 +5,10 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperExperience'] @@ -19,13 +19,15 @@ type Client = ApiClients['shopperExperience'] Either `categoryId` or `productId` must be given in addition to `aspectTypeId`. Because only a single page-to-product and page-to-category assignment per aspect type can be authored today, the returned results contains one element at most. **Important**: Because this resource uses the GET method, you must not pass sensitive data (payment card information, for example) and must not perform any transactional processes within the server-side scripts that are run for the page and components. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Experience `getPages` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-experience?meta=getPages| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperexperience.shopperexperience-1.html#getpages | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const usePages = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -34,15 +36,16 @@ export const usePages = ( const methodName = 'getPages' const requiredParameters = ['organizationId', 'aspectTypeId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -52,13 +55,15 @@ export const usePages = ( * Get a Page Designer page based on a single page ID. The results will apply the visibility rules for the page's components, such as personalization or scheduled visibility. **Important**: Because this resource uses the GET method, you must not pass sensitive data (payment card information, for example) and must not perform any transactional processes within the server-side scripts that are run for the page and components. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Experience `getPage` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-experience?meta=getPage| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperexperience.shopperexperience-1.html#getpage | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const usePage = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -67,15 +72,16 @@ export const usePage = ( const methodName = 'getPage' const requiredParameters = ['organizationId', 'pageId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters diff --git a/packages/commerce-sdk-react/src/hooks/ShopperExperience/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperExperience/queryKeyHelpers.ts index 01a14f5090..94b0028596 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperExperience/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperExperience/queryKeyHelpers.ts @@ -10,10 +10,16 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperExperience<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { - getPages: ['/organizations/', string, '/pages', Params<'getPages'>] - getPage: ['/organizations/', string, '/pages/', string, Params<'getPage'>] + getPages: ['/organizations/', string | undefined, '/pages', Params<'getPages'>] + getPage: [ + '/organizations/', + string | undefined, + '/pages/', + string | undefined, + Params<'getPage'> + ] } // This is defined here, rather than `types.ts`, because it relies on `Client` and `QueryKeys`, diff --git a/packages/commerce-sdk-react/src/hooks/ShopperGiftCertificates/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperGiftCertificates/query.ts index dafd3db2c6..5d454eac6e 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperGiftCertificates/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperGiftCertificates/query.ts @@ -5,23 +5,25 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperGiftCertificates'] /** * Action to retrieve an existing gift certificate. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Gift Certificates `getGiftCertificate` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-gift-certificates?meta=getGiftCertificate| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppergiftcertificates.shoppergiftcertificates-1.html#getgiftcertificate | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useGiftCertificate = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -30,15 +32,16 @@ export const useGiftCertificate = ( const methodName = 'getGiftCertificate' const requiredParameters = ['organizationId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery( + return useQuery( netOptions, { // !!! This is a violation of our design goal of minimal logic in the indivudal endpoint diff --git a/packages/commerce-sdk-react/src/hooks/ShopperGiftCertificates/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperGiftCertificates/queryKeyHelpers.ts index 8c75dff6d7..f428db9e2a 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperGiftCertificates/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperGiftCertificates/queryKeyHelpers.ts @@ -10,11 +10,11 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperGiftCertificates<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { getGiftCertificate: [ '/organizations/', - string, + string | undefined, '/gift-certificate', Params<'getGiftCertificate'> ] diff --git a/packages/commerce-sdk-react/src/hooks/ShopperLogin/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperLogin/query.ts index 83df255c0b..58bb2a7187 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperLogin/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperLogin/query.ts @@ -5,23 +5,25 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperLogin'] /** * Get credential quality statistics for a user. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Login `retrieveCredQualityUserInfo` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-login?meta=retrieveCredQualityUserInfo| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperlogin.shopperlogin-1.html#retrievecredqualityuserinfo | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCredQualityUserInfo = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -30,15 +32,16 @@ export const useCredQualityUserInfo = ( const methodName = 'retrieveCredQualityUserInfo' const requiredParameters = ['organizationId', 'username'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -46,13 +49,15 @@ export const useCredQualityUserInfo = ( } /** * Returns a JSON listing of claims about the currently authenticated user. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Login `getUserInfo` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-login?meta=getUserInfo| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperlogin.shopperlogin-1.html#getuserinfo | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useUserInfo = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -61,15 +66,16 @@ export const useUserInfo = ( const methodName = 'getUserInfo' const requiredParameters = ['organizationId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -77,13 +83,15 @@ export const useUserInfo = ( } /** * Returns a JSON listing of the OpenID/OAuth endpoints, supported scopes and claims, public keys used to sign the tokens, and other details. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Login `getWellknownOpenidConfiguration` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-login?meta=getWellknownOpenidConfiguration| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperlogin.shopperlogin-1.html#getwellknownopenidconfiguration | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useWellknownOpenidConfiguration = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -92,15 +100,16 @@ export const useWellknownOpenidConfiguration = ( const methodName = 'getWellknownOpenidConfiguration' const requiredParameters = ['organizationId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -108,13 +117,15 @@ export const useWellknownOpenidConfiguration = ( } /** * Returns a JSON Web Key Set (JWKS) containing the current, past, and future public keys. The key set enables clients to validate the Shopper JSON Web Token (JWT) issued by SLAS. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Login `getJwksUri` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-login?meta=getJwksUri| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperlogin.shopperlogin-1.html#getjwksuri | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useJwksUri = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -123,15 +134,16 @@ export const useJwksUri = ( const methodName = 'getJwksUri' const requiredParameters = ['organizationId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters diff --git a/packages/commerce-sdk-react/src/hooks/ShopperLogin/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperLogin/queryKeyHelpers.ts index d4c8d92768..86a58a0051 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperLogin/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperLogin/queryKeyHelpers.ts @@ -10,22 +10,22 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperLogin<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { retrieveCredQualityUserInfo: [ '/organizations/', - string, + string | undefined, '/cred-qual/user', Params<'retrieveCredQualityUserInfo'> ] - getUserInfo: ['/organizations/', string, '/oauth2/userinfo', Params<'getUserInfo'>] + getUserInfo: ['/organizations/', string | undefined, '/oauth2/userinfo', Params<'getUserInfo'>] getWellknownOpenidConfiguration: [ '/organizations/', - string, + string | undefined, '/oauth2/.well-known/openid-configuration', Params<'getWellknownOpenidConfiguration'> ] - getJwksUri: ['/organizations/', string, '/oauth2/jwks', Params<'getJwksUri'>] + getJwksUri: ['/organizations/', string | undefined, '/oauth2/jwks', Params<'getJwksUri'>] } // This is defined here, rather than `types.ts`, because it relies on `Client` and `QueryKeys`, diff --git a/packages/commerce-sdk-react/src/hooks/ShopperOrders/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperOrders/query.ts index 6a5a8e30b0..788957da47 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperOrders/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperOrders/query.ts @@ -5,23 +5,25 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperOrders'] /** * Gets information for an order. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Orders `getOrder` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-orders?meta=getOrder| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperorders.shopperorders-1.html#getorder | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useOrder = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -30,15 +32,16 @@ export const useOrder = ( const methodName = 'getOrder' const requiredParameters = ['organizationId', 'orderNo', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -46,13 +49,15 @@ export const useOrder = ( } /** * Gets the applicable payment methods for an existing order considering the open payment amount only. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Orders `getPaymentMethodsForOrder` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-orders?meta=getPaymentMethodsForOrder| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperorders.shopperorders-1.html#getpaymentmethodsfororder | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const usePaymentMethodsForOrder = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -61,15 +66,16 @@ export const usePaymentMethodsForOrder = ( const methodName = 'getPaymentMethodsForOrder' const requiredParameters = ['organizationId', 'orderNo', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -79,13 +85,15 @@ export const usePaymentMethodsForOrder = ( * This method gives you the external taxation data of the order transferred from the basket during order creation. This endpoint can be called only if external taxation was used. See POST /baskets for more information. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Orders `getTaxesFromOrder` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-orders?meta=getTaxesFromOrder| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperorders.shopperorders-1.html#gettaxesfromorder | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useTaxesFromOrder = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -94,15 +102,16 @@ export const useTaxesFromOrder = ( const methodName = 'getTaxesFromOrder' const requiredParameters = ['organizationId', 'orderNo', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters diff --git a/packages/commerce-sdk-react/src/hooks/ShopperOrders/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperOrders/queryKeyHelpers.ts index 00a64a413f..8ffc7827f5 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperOrders/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperOrders/queryKeyHelpers.ts @@ -10,22 +10,28 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperOrders<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { - getOrder: ['/organizations/', string, '/orders/', string, Params<'getOrder'>] + getOrder: [ + '/organizations/', + string | undefined, + '/orders/', + string | undefined, + Params<'getOrder'> + ] getPaymentMethodsForOrder: [ '/organizations/', - string, + string | undefined, '/orders/', - string, + string | undefined, '/payment-methods', Params<'getPaymentMethodsForOrder'> ] getTaxesFromOrder: [ '/organizations/', - string, + string | undefined, '/orders/', - string, + string | undefined, '/taxes', Params<'getTaxesFromOrder'> ] diff --git a/packages/commerce-sdk-react/src/hooks/ShopperProducts/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperProducts/query.ts index 32500f7819..ece6bec907 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperProducts/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperProducts/query.ts @@ -5,23 +5,25 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperProducts'] /** * Allows access to multiple products by a single request. Only products that are online and assigned to a site catalog are returned. The maximum number of productIDs that can be requested are 24. Along with product details, the availability, product options, images, price, promotions, and variations for the valid products will be included, as appropriate. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Products `getProducts` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-products?meta=getProducts| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperproducts.shopperproducts-1.html#getproducts | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useProducts = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -30,15 +32,16 @@ export const useProducts = ( const methodName = 'getProducts' const requiredParameters = ['organizationId', 'ids', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -46,13 +49,15 @@ export const useProducts = ( } /** * Allows access to product details for a single product ID. Only products that are online and assigned to a site catalog are returned. Along with product details, the availability, images, price, bundled_products, set_products, recommedations, product options, variations, and promotions for the products will be included, as appropriate. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Products `getProduct` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-products?meta=getProduct| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperproducts.shopperproducts-1.html#getproduct | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useProduct = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -61,15 +66,16 @@ export const useProduct = ( const methodName = 'getProduct' const requiredParameters = ['organizationId', 'id', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -77,13 +83,15 @@ export const useProduct = ( } /** * When you use the URL template, the server returns multiple categories (a result object of category documents). You can use this template as a convenient way of obtaining multiple categories in a single request, instead of issuing separate requests for each category. You can specify up to 50 multiple IDs. You must enclose the list of IDs in parentheses. If a category identifier contains parenthesis or the separator sign, you must URL encode the character. The server only returns online categories. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Products `getCategories` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-products?meta=getCategories| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperproducts.shopperproducts-1.html#getcategories | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCategories = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -92,15 +100,16 @@ export const useCategories = ( const methodName = 'getCategories' const requiredParameters = ['organizationId', 'ids', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -110,13 +119,15 @@ export const useCategories = ( * When you use the URL template below, the server returns a category identified by its ID; by default, the server also returns the first level of subcategories, but you can specify another level by setting the levels parameter. The server only returns online categories. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Products `getCategory` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-products?meta=getCategory| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperproducts.shopperproducts-1.html#getcategory | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useCategory = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -125,15 +136,16 @@ export const useCategory = ( const methodName = 'getCategory' const requiredParameters = ['organizationId', 'id', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters diff --git a/packages/commerce-sdk-react/src/hooks/ShopperProducts/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperProducts/queryKeyHelpers.ts index e3a07e4134..7e0d8812f0 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperProducts/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperProducts/queryKeyHelpers.ts @@ -10,12 +10,24 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperProducts<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { - getProducts: ['/organizations/', string, '/products', Params<'getProducts'>] - getProduct: ['/organizations/', string, '/products/', string, Params<'getProduct'>] - getCategories: ['/organizations/', string, '/categories', Params<'getCategories'>] - getCategory: ['/organizations/', string, '/categories/', string, Params<'getCategory'>] + getProducts: ['/organizations/', string | undefined, '/products', Params<'getProducts'>] + getProduct: [ + '/organizations/', + string | undefined, + '/products/', + string | undefined, + Params<'getProduct'> + ] + getCategories: ['/organizations/', string | undefined, '/categories', Params<'getCategories'>] + getCategory: [ + '/organizations/', + string | undefined, + '/categories/', + string | undefined, + Params<'getCategory'> + ] } // This is defined here, rather than `types.ts`, because it relies on `Client` and `QueryKeys`, diff --git a/packages/commerce-sdk-react/src/hooks/ShopperPromotions/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperPromotions/query.ts index 42c63913e8..00310aaa0f 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperPromotions/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperPromotions/query.ts @@ -5,23 +5,25 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperPromotions'] /** * Returns an array of enabled promotions for a list of specified IDs. In the request URL, you can specify up to 50 IDs. If you specify an ID that contains either parentheses or the separator characters, you must URL encode these characters. Each request returns only enabled promotions as the server does not consider promotion qualifiers or schedules. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Promotions `getPromotions` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-promotions?meta=getPromotions| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperpromotions.shopperpromotions-1.html#getpromotions | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const usePromotions = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -30,15 +32,16 @@ export const usePromotions = ( const methodName = 'getPromotions' const requiredParameters = ['organizationId', 'siteId', 'ids'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -50,13 +53,15 @@ criteria. In the request URL, you must provide a campaign_id parameter, and you range by providing start_date and end_date parameters. Both parameters are required to specify a date range, as omitting one causes the server to return a MissingParameterException fault. Each request returns only enabled promotions, since the server does not consider promotion qualifiers or schedules. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Promotions `getPromotionsForCampaign` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-promotions?meta=getPromotionsForCampaign| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperpromotions.shopperpromotions-1.html#getpromotionsforcampaign | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const usePromotionsForCampaign = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -65,15 +70,16 @@ export const usePromotionsForCampaign = ( const methodName = 'getPromotionsForCampaign' const requiredParameters = ['organizationId', 'campaignId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters diff --git a/packages/commerce-sdk-react/src/hooks/ShopperPromotions/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperPromotions/queryKeyHelpers.ts index 6ed70a9589..9798133161 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperPromotions/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperPromotions/queryKeyHelpers.ts @@ -10,14 +10,14 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperPromotions<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { - getPromotions: ['/organizations/', string, '/promotions', Params<'getPromotions'>] + getPromotions: ['/organizations/', string | undefined, '/promotions', Params<'getPromotions'>] getPromotionsForCampaign: [ '/organizations/', - string, + string | undefined, '/promotions/campaigns/', - string, + string | undefined, Params<'getPromotionsForCampaign'> ] } diff --git a/packages/commerce-sdk-react/src/hooks/ShopperSearch/query.ts b/packages/commerce-sdk-react/src/hooks/ShopperSearch/query.ts index 78ac9e3f67..97f80cd20c 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperSearch/query.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperSearch/query.ts @@ -5,10 +5,10 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {UseQueryResult} from '@tanstack/react-query' -import {ApiClients, ApiQueryOptions, Argument, DataType} from '../types' +import {ApiClients, ApiQueryOptions, Argument, DataType, NullableParameters} from '../types' import useCommerceApi from '../useCommerceApi' import {useQuery} from '../useQuery' -import {mergeOptions} from '../utils' +import {mergeOptions, omitNullableParameters} from '../utils' import * as queryKeyHelpers from './queryKeyHelpers' type Client = ApiClients['shopperSearch'] @@ -16,13 +16,15 @@ type Client = ApiClients['shopperSearch'] /** * Provides keyword and refinement search functionality for products. Only returns the product ID, link, and name in the product search hit. The search result contains only products that are online and assigned to site catalog. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Search `productSearch` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-search?meta=productSearch| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppersearch.shoppersearch-1.html#productsearch | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useProductSearch = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -31,15 +33,16 @@ export const useProductSearch = ( const methodName = 'productSearch' const requiredParameters = ['organizationId', 'siteId'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters @@ -47,13 +50,15 @@ export const useProductSearch = ( } /** * Provides keyword search functionality for products, categories, and brands suggestions. Returns suggested products, suggested categories, and suggested brands for the given search phrase. + * @parameter apiOptions - Options to pass through to `commerce-sdk-isomorphic`, with `null` accepted for unset API parameters. + * @parameter queryOptions - TanStack Query query options, with `enabled` by default set to check that all required API parameters have been set. * @returns A TanStack Query query hook with data from the Shopper Search `getSearchSuggestions` endpoint. * @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-search?meta=getSearchSuggestions| Salesforce Developer Center} for more information about the API endpoint. * @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shoppersearch.shoppersearch-1.html#getsearchsuggestions | `commerce-sdk-isomorphic` documentation} for more information on the parameters and returned data type. * @see {@link https://tanstack.com/query/latest/docs/react/reference/useQuery | TanStack Query `useQuery` reference} for more information about the return value. */ export const useSearchSuggestions = ( - apiOptions: Argument, + apiOptions: NullableParameters>, queryOptions: ApiQueryOptions = {} ): UseQueryResult> => { type Options = Argument @@ -62,15 +67,16 @@ export const useSearchSuggestions = ( const methodName = 'getSearchSuggestions' const requiredParameters = ['organizationId', 'siteId', 'q'] as const - // Parameters can be set in `apiOptions` or `client.clientConfig`, we must merge them in order - // to generate the correct query key. - const netOptions = mergeOptions(client, apiOptions) + // Parameters can be set in `apiOptions` or `client.clientConfig`; + // we must merge them in order to generate the correct query key. + const netOptions = omitNullableParameters(mergeOptions(client, apiOptions)) const queryKey = queryKeyHelpers[methodName].queryKey(netOptions.parameters) + // We don't use `netOptions` here because we manipulate the options in `useQuery`. const method = async (options: Options) => await client[methodName](options) // For some reason, if we don't explicitly set these generic parameters, the inferred type for // `Data` sometimes, but not always, includes `Response`, which is incorrect. I don't know why. - return useQuery(netOptions, queryOptions, { + return useQuery(netOptions, queryOptions, { method, queryKey, requiredParameters diff --git a/packages/commerce-sdk-react/src/hooks/ShopperSearch/queryKeyHelpers.ts b/packages/commerce-sdk-react/src/hooks/ShopperSearch/queryKeyHelpers.ts index 43259012fd..c02fdab958 100644 --- a/packages/commerce-sdk-react/src/hooks/ShopperSearch/queryKeyHelpers.ts +++ b/packages/commerce-sdk-react/src/hooks/ShopperSearch/queryKeyHelpers.ts @@ -10,12 +10,17 @@ import {pick} from '../utils' // We must use a client with no parameters in order to have required/optional match the API spec type Client = ShopperSearch<{shortCode: string}> -type Params = NonNullable['parameters']> +type Params = Partial['parameters']> export type QueryKeys = { - productSearch: ['/organizations/', string, '/product-search', Params<'productSearch'>] + productSearch: [ + '/organizations/', + string | undefined, + '/product-search', + Params<'productSearch'> + ] getSearchSuggestions: [ '/organizations/', - string, + string | undefined, '/search-suggestions', Params<'getSearchSuggestions'> ] diff --git a/packages/commerce-sdk-react/src/hooks/types.ts b/packages/commerce-sdk-react/src/hooks/types.ts index 9a470d9b26..500a317403 100644 --- a/packages/commerce-sdk-react/src/hooks/types.ts +++ b/packages/commerce-sdk-react/src/hooks/types.ts @@ -54,6 +54,17 @@ export type ExcludeTail = T extends readonly [...i : Readonly : T // If it's a plain array, rather than a tuple, then removing the last element has no effect +/** Adds `null` as an allowed value to all properties. */ +type AllowNull = {[K in keyof T]: T[K] | null} + +/** Gets the keys of `T` which allow `null` as a possible value. */ +type NullKeys = {[K in keyof T]-?: null extends T[K] ? K : never}[keyof T] + +/** Removes `null` values and marks those properties as optional. */ +export type NullToOptional = Omit> & { + [K in keyof T]?: NonNullable +} + // --- API CLIENTS --- // export type ApiParameter = string | number | boolean | string[] | number[] @@ -131,7 +142,7 @@ export type MergedOptions /** Query key interface used by API query hooks. */ export type ApiQueryKey = Record> = - readonly [...path: string[], parameters: Params] + readonly [...path: (string | undefined)[], parameters: Params] /** Query options for endpoint hooks. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -142,6 +153,17 @@ export type ApiQueryOptions> = Prettify< > > +/** Adds `null` as an allowed value to all parameters. */ +export type NullableParameters = { + // This `extends` check allows us to more easily preserve required/optional parameters + [K in keyof T]: K extends 'parameters' ? AllowNull : T[K] +} + +/** Remove `null` and `undefined` values from all parameters. */ +export type OmitNullableParameters = Omit & { + parameters: NullToOptional +} + // --- CACHE HELPERS --- // /** diff --git a/packages/commerce-sdk-react/src/hooks/useQuery.ts b/packages/commerce-sdk-react/src/hooks/useQuery.ts index 5b61be7179..41bdc1abb5 100644 --- a/packages/commerce-sdk-react/src/hooks/useQuery.ts +++ b/packages/commerce-sdk-react/src/hooks/useQuery.ts @@ -6,7 +6,16 @@ */ import {useQuery as useReactQuery} from '@tanstack/react-query' import {useAuthorizationHeader} from './useAuthorizationHeader' -import {ApiMethod, ApiOptions, ApiQueryKey, ApiQueryOptions} from './types' +import { + ApiClient, + ApiMethod, + ApiOptions, + ApiQueryKey, + ApiQueryOptions, + MergedOptions, + NullableParameters, + OmitNullableParameters +} from './types' import {hasAllKeys} from './utils' /** @@ -16,24 +25,36 @@ import {hasAllKeys} from './utils' * @param hookConfig - Config values that vary per API endpoint * @internal */ -export const useQuery = ( - apiOptions: Options, +export const useQuery = ( + // `OmitNullableParameters>` has the net result of marking parameters + // as optional if they are required in `Options` and NOT required in `Client`. + apiOptions: OmitNullableParameters>>, queryOptions: ApiQueryOptions>, hookConfig: { method: ApiMethod - queryKey: ApiQueryKey> + queryKey: ApiQueryKey> requiredParameters: ReadonlyArray> enabled?: boolean } ) => { const authenticatedMethod = useAuthorizationHeader(hookConfig.method) - return useReactQuery(hookConfig.queryKey, () => authenticatedMethod(apiOptions), { + // This type assertion is NOT safe in all cases. However, we know that `requiredParameters` is + // the list of parameters required by `Options`, and we know that in the default case (when + // `queryOptions.enabled` is not set), we only execute the hook when `apiOptions` has all + // required parameters. Therefore, we know that `apiOptions` satisfies `Options` in the default + // case, so the type assertion is safe in the default case. We explicitly do NOT guarantee type + // safety when `queryOptions.enabled` is set; when it is `true`, the callback may be called with + // missing parameters. This will result in a runtime error. I think that this is an acceptable + // trade-off, as the behavior is opt-in by the end user, and it feels like adding type safety + // for this case would add significantly more complexity. + const wrappedMethod = async () => await authenticatedMethod(apiOptions as Options) + return useReactQuery(hookConfig.queryKey, wrappedMethod, { enabled: // Individual hooks can provide `enabled` checks that are done in ADDITION to // the required parameter check hookConfig.enabled !== false && // The default `enabled` is "has all required parameters" - hasAllKeys(apiOptions.parameters ?? {}, hookConfig.requiredParameters), + hasAllKeys(apiOptions.parameters, hookConfig.requiredParameters), // End users can always completely OVERRIDE the default `enabled` check ...queryOptions }) diff --git a/packages/commerce-sdk-react/src/hooks/utils.ts b/packages/commerce-sdk-react/src/hooks/utils.ts index f65d339dd7..07c99e2485 100644 --- a/packages/commerce-sdk-react/src/hooks/utils.ts +++ b/packages/commerce-sdk-react/src/hooks/utils.ts @@ -5,7 +5,15 @@ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ import {Query, QueryClient} from '@tanstack/react-query' -import {ApiClient, ApiOptions, ApiParameter, CacheUpdate, MergedOptions} from './types' +import { + ApiClient, + ApiOptions, + ApiParameter, + CacheUpdate, + MergedOptions, + NullToOptional, + OmitNullableParameters +} from './types' /** Applies the set of cache updates to the query client. */ export const updateCache = (queryClient: QueryClient, cacheUpdates: CacheUpdate, data: unknown) => { @@ -38,17 +46,17 @@ export const isObject = (obj: unknown): obj is Record => /** Determines whether a value has all of the given keys. */ export const hasAllKeys = (object: T, keys: ReadonlyArray): boolean => - keys.every((key) => object[key] !== undefined) + keys.every((key) => object[key] !== undefined && object[key] !== null) /** Creates a query predicate that determines whether a query key starts with the given path segments. */ export const pathStartsWith = - (search: readonly string[]) => + (search: readonly (string | undefined)[]) => ({queryKey}: Query): boolean => queryKey.length >= search.length && search.every((lookup, idx) => queryKey[idx] === lookup) /** Creates a query predicate that determines whether a query key fully matches the given path segments. */ export const matchesPath = - (search: readonly string[]) => + (search: readonly (string | undefined)[]) => ({queryKey}: Query): boolean => // ApiQueryKey = [...path, parameters] queryKey.length === 1 + search.length && @@ -99,18 +107,6 @@ export const matchParameters = ( return matchParametersStrict(search) } -/** Creates a query predicate that matches against common API config parameters. */ -export const matchesApiConfig = (parameters: Record) => - matchParameters(parameters, [ - // NOTE: `shortCode` and `version` are omitted, as query keys are constructed from endpoint - // paths, but the two paarameters are only used to construct the base URI. - 'clientId', - 'currency', // TODO: maybe? - 'locale', // TODO: maybe? - 'organizationId', - 'siteId' - ]) - /** Creates a query predicate that returns true if all of the given predicates return true. */ export const and = (...funcs: Array<(...args: Args) => boolean>) => @@ -134,7 +130,8 @@ export const mergeOptions = ( obj: T, keys: readonly K[] ): Pick => { - const picked = {} as Pick // Assertion is not true, yet, but we make it so! + // Assertion is not true, yet, but we make it so! + const picked = {} as Pick keys.forEach((key) => { if (key in obj) { + // Skip assigning optional/missing parameters picked[key] = obj[key] } }) return picked } + +/** Removes keys with `null` or `undefined` values from the given object. */ +export const omitNullable = (obj: T): NullToOptional => { + // Assertion is not true, yet, but we make it so! + const stripped = {} as NullToOptional + // Assertion because `Object.entries` is limited :\ + const entries = Object.entries(obj) as Array<[keyof T, T[keyof T]]> + for (const [key, value] of entries) { + if (value !== null && value !== undefined) stripped[key] = value + } + return stripped +} + +/** Removes keys with `null` or `undefined` values from the `parameters` of the given object. */ +export const omitNullableParameters = ( + obj: T +): OmitNullableParameters => ({ + ...obj, + // Without the explicit generic parameter, the generic is inferred as `object`, + // the connection to `T` is lost, and TypeScript complains. + parameters: omitNullable(obj.parameters) +})