Skip to content
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

Closed
wants to merge 16 commits into from

Conversation

wjhsf
Copy link
Contributor

@wjhsf wjhsf commented Jan 31, 2023

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 helper useQuery/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 new Test directory. Once the design is more finalized, I'll remove Test and generate the real hooks.

Description

Types of Changes

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Documentation update
  • Breaking change (could cause existing functionality to not work as expected)
  • Other changes (non-breaking changes that does not fit any of the above)

Breaking changes include:

  • Removing a public function or component or prop
  • Adding a required argument to a function
  • Changing the data type of a function parameter or return value
  • Adding a new peer dependency to package.json

Changes

  • (change1)

How to Test-Drive This PR

  • (step1)

Checklists

General

  • Changes are covered by test cases
  • CHANGELOG.md updated with a short description of changes (not required for documentation updates)

Accessibility Compliance

You must check off all items in one of the follow two lists:

  • There are no changes to UI

or...

Localization

  • Changes include a UI text update in the Retail React App (which requires translation)

Copy link
Contributor Author

@wjhsf wjhsf left a 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).

Comment on lines 21 to 24
export const useBasket = (
apiOptions: Argument<Client['getBasket']>,
queryOptions: UseQueryOptions<DataType<Client['getBasket']>>
): UseQueryResult<DataType<Client['getBasket']>> => {
Copy link
Contributor Author

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.

Comment on lines 25 to 41
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
}
)
Copy link
Contributor Author

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']
Copy link
Contributor Author

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 @@
/*
Copy link
Contributor Author

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 @@
/*
Copy link
Contributor Author

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!

Comment on lines 56 to 65
* 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>
Copy link
Contributor Author

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>(
Copy link
Contributor Author

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>(
Copy link
Contributor Author

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.

Comment on lines 19 to 26
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
}
Copy link
Contributor Author

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
Copy link
Contributor Author

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(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines 42 to 158
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
}
)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: this PR has a title simplify hooks and no description. Are we modifying only existing hooks or are we adding new ones?

When the PR title says "simplify" and the diff shows +658 that's confusing to me

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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.
  2. 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.

Comment on lines +31 to +47
[
'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
Copy link
Contributor Author

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.

@alexvuong
Copy link
Collaborator

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

@wjhsf wjhsf mentioned this pull request Feb 3, 2023
12 tasks
@wjhsf wjhsf closed this Feb 6, 2023
@wjhsf wjhsf deleted the wjh/simplify-hooks branch March 15, 2023 22:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants