SalesforceCommerceCloud / pwa-kit

React-based JavaScript frontend framework to create a progressive web app (PWA) storefront for Salesforce B2C Commerce.
https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/pwa-kit-overview.html
BSD 3-Clause "New" or "Revised" License
284 stars 134 forks source link

[FEATURE] Add more details about API error #1659

Open LucasReyre opened 9 months ago

LucasReyre commented 9 months ago

Is your feature request related to a problem? Please describe.

It seems that there is currently no way to retrieve the body of an API request in error. This is a real pain point as I have customized some SCAPI endpoint and return custom status code like :

{
    "title": "Hook Status",
    "type": "https://api.commercecloud.salesforce.com/documentation/error/v1/errors/hook-status",
    "detail": "An error occurred in ExtensionPoint 'dw.ocapi.shop.basket.items.afterPOST'.",
    "extensionPoint": "dw.ocapi.shop.basket.items.afterPOST",
    "statusCode": "add_to_cart_not_allowed",
    "statusMessage": "add_to_cart_not_allowed",
    "statusDetails": {}
}

And I would like to display custom messages on the front-end depending on this statusCode.

Describe the solution you'd like I would like the possibility to get back the API's body from the SDK.

johnboxall commented 9 months ago

Thanks @LucasReyre.

Using SCAPI hooks to add details to error message with Script API's Status and status.addDetail is very useful to provide support for conditional error messages based on SCAPI hook invocation success.

If you drop down to the level of commerce-sdk-isomorphic are you able to see the error message?

I'm curious about where in the stack you've observed the details being lost.

Thanks!

LucasReyre commented 9 months ago

I'm not comfortable enough with commerce-sdk to investigate this module. But I have observed that in the pwa-kit addItemToBasketMutation.mutateAsync throws the following exception when a response other than in success is received.

image

johnboxall commented 8 months ago

To build on the issue, commerce-sdk-isomorphic does provide access to custom validation errors returned by SCAPI, so we should validate that the error body is available in commerce-sdk-react.

To replicate ...

  1. In a cartridge, create a hook that throw an error:
// dw.ocapi.shop.category.beforeGET
exports.beforeGET = function errorWithDetails() {
    const status = new Status(Status.ERROR)
    status.addDetail("validation_error", "Naughty Category")
    return status
}
  1. In the ISO SDK, you can see you can pull out the error:
import capi from "commerce-sdk-isomorphic"

function getClientConfig() {
    return {
        parameters: {
            shortCode: "kv7kzm78",
            organizationId: "f_ecom_zzrf_016",
            siteId: "RefArch",
            clientId: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
        },
        throwOnBadResponse: true,
    }
}

async function main() {
    const clientConfig = getClientConfig()
    const shopperLoginClient = new capi.ShopperLogin(clientConfig)

    // 1️⃣ Login as a guest.
    const guestTokenResponse = await capi.helpers.loginGuestUserPrivate(
        shopperLoginClient,
        {},
        { clientSecret: "REDACTED" }
    )
    clientConfig.headers = {
        Authorization: `Bearer ${guestTokenResponse.access_token}`,
    }

    // 2️⃣ Make a call to categories which throws an error.
    try {
        await new capi.ShopperProducts(clientConfig).getCategory({
            parameters: { id: "root", levels: 0 },
        })
    } catch (err) {
        console.error({ errData: await err.response.json() })
    }
}

main()
kevinxh commented 8 months ago

Hi @LucasReyre @johnboxall !

Thank you for bringing this up. To build on John's point, you can grab error from the commerce-sdk-isomorphic level but it's very unintuitive to access errors from the commerce-sdk-react hooks.

Yes, there is a way to access API errors!

There is a way to grab errors and althou it isn't the prettiest way.

const product = useProduct(
        {parameters: {id: '25502228Mxxx'}},
        {
            onError: async (error) => {
                // access error properties in json
                const json = await error.response.json()
                console.log(json)
            }
        }
    )

CleanShot 2024-03-18 at 10 22 11

There is a playground app where you can reproduce this -> https://github.com/SalesforceCommerceCloud/pwa-kit/compare/demo-commerce-sdk-react-error-handling?expand=1

But, it should be better!

As you may already found out, the above method uses a callback function that doesn't play well with react states.

Ideally it should be very easy to access errors like this:

const product = useProduct({parameters: {id: '25502228Mxxx'}})

console.log(product.error)

I'm going to create a bug ticket in our system to fix this issue. In the meantime, please use the callback method to access error. Thank you!

LucasReyre commented 8 months ago

Hello @kevinxh

The way you catch the error details works well, I will use it until you release something easier.

Thanks to you two