-
Notifications
You must be signed in to change notification settings - Fork 146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wjh/simplify hooks #954
Wjh/simplify hooks #954
Conversation
Notably, this allows the correct type of the query key to be passed through to the callback function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore the test files (inconsequential changes) and the mutation/action files for now (not yet completed).
export const useBasket = ( | ||
apiOptions: Argument<Client['getBasket']>, | ||
queryOptions: UseQueryOptions<DataType<Client['getBasket']>> | ||
): UseQueryResult<DataType<Client['getBasket']>> => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Design goal: Simple function signature - just pass through apiOptions
to commerce-sdk-isomorphic
and queryOptions
to @tanstack/react-query
.
const requiredParameters = ['organizationId', 'basketId', 'siteId'] as const | ||
const queryKey = ['TODO'] | ||
const {shopperBaskets: client} = useCommerceApi() | ||
const method = client.getBasket.bind(client) | ||
|
||
return useQuery( | ||
apiOptions, | ||
{ | ||
queryKey, | ||
...queryOptions | ||
}, | ||
{ | ||
client, | ||
method, | ||
requiredParameters | ||
} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Design goal: Maximum DRY by only declaring endpoint-specific variables in one spot inside the function, and putting all the logic in the helper hook.
queryOptions: UseQueryOptions<DataType<Client['getBasket']>> | ||
): UseQueryResult<DataType<Client['getBasket']>> => { | ||
const requiredParameters = ['organizationId', 'basketId', 'siteId'] as const | ||
const queryKey = ['TODO'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not planning on changing the query keys from what they currently are (for now), but there's currently no logic in the SDK generation tools to replicate them. (It won't be hard to add, but that's a tomorrow task.)
@@ -0,0 +1,158 @@ | |||
/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is automatically generated from the RAML spec!
@@ -0,0 +1,364 @@ | |||
/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore this for now. I haven't updated it since July, so it's very out of date!
* Adds additional parameters to a function signature. To preserve named parameters | ||
* in the returned type, pass a named tuple for the extra parameters. | ||
*/ | ||
export type IMutationFunction<TData = unknown, TVariables = unknown> = ( | ||
variables: TVariables, | ||
apiClients: ApiClients | ||
) => Promise<TData> | ||
|
||
/** | ||
* Modified version of React Query's Query Function. Added a second argument | ||
* API clients. | ||
*/ | ||
export type IQueryFunction<TData = unknown> = ( | ||
context: QueryFunctionContext<QueryKey>, | ||
apiClients: ApiClients | ||
) => Promise<TData> | ||
export type AddParameters< | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
Fn extends (...args: any[]) => unknown, | ||
Extra extends unknown[] | ||
// TODO: Remove this after merging in prettier v2 changes | ||
// eslint-disable-next-line prettier/prettier | ||
> = (...newArgs: [...originalArgs: Parameters<Fn>, ...extra: Extra]) => ReturnType<Fn> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created this for one iteration, evidently forgot to remove it.
import useAuth from './useAuth' | ||
|
||
// This feels like a bad name | ||
export const useAuthorizationHeader = <Arg extends {headers?: Record<string, string>}, Ret>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically what useAuthenticatedClient
does, but sets the authorization header on the method headers, rather than the config headers.
[apiClients: ApiClients] | ||
> | ||
|
||
export const useMutation = <Data, Err, Vars>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was my first pass at cleaning up the existing types. I'll likely end up changing this a bit, as I did with useQuery
.
apiOptions: ApiArg, | ||
queryOptions: UseQueryOptions<Data, Err, Data, QK> & {queryKey: QK}, | ||
hookConfig: { | ||
client: Client | ||
method: (arg: ApiArg) => Promise<Data> | ||
requiredParameters: ReadonlyArray<keyof ApiArg['parameters']> | ||
enabled?: boolean | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Design goal: Mimic the format of the generated hooks. The third parameter is the variables we pass through from the generated hook in order to minimize repetitive logic in them.
client: Client | ||
method: (arg: ApiArg) => Promise<Data> | ||
requiredParameters: ReadonlyArray<keyof ApiArg['parameters']> | ||
enabled?: boolean |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intended to be used for extra "default" enabled checks, in addition to the "has all required parameters" logic, so that we don't have to duplicate the required parameter check outside this hook. For example, useCustomer
might have enabled: customerType === 'registered'
. The end user can still fully override using queryOptions.enabled
.
|
||
const tests = (Object.keys(mutationPayloads) as ShopperBasketsMutationType[]).map( | ||
const tests = (Object.keys(mutationPayloads) as Array<keyof typeof mutationPayloads>).map( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
return useQuery( | ||
apiOptions, | ||
{ | ||
queryKey, | ||
...queryOptions | ||
}, | ||
{ | ||
client, | ||
method, | ||
requiredParameters | ||
} | ||
) | ||
} | ||
/** | ||
* A hook for `ShopperBaskets#getPriceBooksForBasket`. | ||
* Gets applicable price books for an existing basket. | ||
* @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=getPriceBooksForBasket} for more information about the API endpoint. | ||
* @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperbaskets.shopperbaskets-1.html#getpricebooksforbasket} for more information on the parameters and returned data type. | ||
* @returns An object describing the state of the request. | ||
*/ | ||
export const usePriceBooksForBasket = ( | ||
apiOptions: Argument<Client['getPriceBooksForBasket']>, | ||
queryOptions: UseQueryOptions<DataType<Client['getPriceBooksForBasket']>> | ||
): UseQueryResult<DataType<Client['getPriceBooksForBasket']>> => { | ||
const requiredParameters = ['organizationId', 'basketId', 'siteId'] as const | ||
const queryKey = ['TODO'] | ||
const {shopperBaskets: client} = useCommerceApi() | ||
const method = client.getPriceBooksForBasket.bind(client) | ||
|
||
return useQuery( | ||
apiOptions, | ||
{ | ||
queryKey, | ||
...queryOptions | ||
}, | ||
{ | ||
client, | ||
method, | ||
requiredParameters | ||
} | ||
) | ||
} | ||
/** | ||
* A hook for `ShopperBaskets#getShippingMethodsForShipment`. | ||
* Gets the applicable shipping methods for a certain shipment of a basket. | ||
* @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=getShippingMethodsForShipment} for more information about the API endpoint. | ||
* @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperbaskets.shopperbaskets-1.html#getshippingmethodsforshipment} for more information on the parameters and returned data type. | ||
* @returns An object describing the state of the request. | ||
*/ | ||
export const useShippingMethodsForShipment = ( | ||
apiOptions: Argument<Client['getShippingMethodsForShipment']>, | ||
queryOptions: UseQueryOptions<DataType<Client['getShippingMethodsForShipment']>> | ||
): UseQueryResult<DataType<Client['getShippingMethodsForShipment']>> => { | ||
const requiredParameters = ['organizationId', 'basketId', 'shipmentId', 'siteId'] as const | ||
const queryKey = ['TODO'] | ||
const {shopperBaskets: client} = useCommerceApi() | ||
const method = client.getShippingMethodsForShipment.bind(client) | ||
|
||
return useQuery( | ||
apiOptions, | ||
{ | ||
queryKey, | ||
...queryOptions | ||
}, | ||
{ | ||
client, | ||
method, | ||
requiredParameters | ||
} | ||
) | ||
} | ||
/** | ||
* A hook for `ShopperBaskets#getTaxesFromBasket`. | ||
* 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. | ||
* @see {@link https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-baskets?meta=getTaxesFromBasket} for more information about the API endpoint. | ||
* @see {@link https://salesforcecommercecloud.github.io/commerce-sdk-isomorphic/classes/shopperbaskets.shopperbaskets-1.html#gettaxesfrombasket} for more information on the parameters and returned data type. | ||
* @returns An object describing the state of the request. | ||
*/ | ||
export const useTaxesFromBasket = ( | ||
apiOptions: Argument<Client['getTaxesFromBasket']>, | ||
queryOptions: UseQueryOptions<DataType<Client['getTaxesFromBasket']>> | ||
): UseQueryResult<DataType<Client['getTaxesFromBasket']>> => { | ||
const requiredParameters = ['organizationId', 'basketId', 'siteId'] as const | ||
const queryKey = ['TODO'] | ||
const {shopperBaskets: client} = useCommerceApi() | ||
const method = client.getTaxesFromBasket.bind(client) | ||
|
||
return useQuery( | ||
apiOptions, | ||
{ | ||
queryKey, | ||
...queryOptions | ||
}, | ||
{ | ||
client, | ||
method, | ||
requiredParameters | ||
} | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Because it's a WIP that I threw up at 5PM before vanishing for a day. Figured it's better to have code with no context than no code at all.
- It's a WIP. Note that the folder the new hook is in is
Test
; the actual hooks haven't been touched yet. I'm looking more for feedback on design than doing the actual code changes.
[ | ||
'https://', | ||
parameters.shortCode, | ||
'.api.commercecloud.salesforce.com/checkout/shopper-baskets/', | ||
parameters.version, | ||
'/organizations/', | ||
parameters.organizationId, | ||
'/baskets/', | ||
parameters.basketId, | ||
'?', | ||
'siteId', | ||
parameters.siteId, | ||
'locale', | ||
parameters.locale, | ||
// Full parameters last for easy lookup | ||
parameters | ||
] as const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used the full generated URL here as the query key. I can't imagine shortCode
or version
ever changing, so we probably don't need the base URI part. Definitely need path and query parameters. Not sure what the optimal format is, though.
This refactor should branch off the develop instead of the feature branch, the goal of the feature branch is to integration the hooks into the Retail App. Technically, it should not involve too much of a change from the commerce-sdk library. Besides, the feature branch is a long-run branch for this release, so I don't this we gonna merge it any time soon, and this refactor is likely to merge before the feature branch. Making refactor from develop is also better because users can actually get to test out new code change when we do another release instead of having to wait until the feature branch is fully completed. We can always merge this back into feature branch when it is done, it is easier than another way around |
This PR is a draft of the changes to simplify the SCAPI hooks. The two primary goals are to fix the function signature (current
develop
has duplicate arguments and unnecessary complexity) and to move as much logic as possible from the endpoint hooks (which are generated) into the helperuseQuery
/useMutation
hooks.Very much still a WIP, but early feedback is appreciated! Currently only the query hooks are done, I haven't touched mutation hooks yet.
To avoid confusing myself during development, I've generated the
ShopperBaskets
hooks in a newTest
directory. Once the design is more finalized, I'll removeTest
and generate the real hooks.Description
Types of Changes
Changes
How to Test-Drive This PR
Checklists
General
Accessibility Compliance
You must check off all items in one of the follow two lists:
or...
Localization