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

Implement dynamic update routes #2290

Draft
wants to merge 19 commits into
base: route-serialization-to-support-shopper-seo-integration
Choose a base branch
from

Conversation

hajinsuha1
Copy link
Collaborator

@hajinsuha1 hajinsuha1 commented Feb 26, 2025

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

  • add extension-seo-url-mapping extension
    • overrides the extendRoutes method to make a Shopper SEO getUrlMapping API call and add it to the routes if a response is returned
  • update pwa-kit-react-sdk to serialize and deserialize routes to allow dynamically modifying the routes list

How to Test-Drive This PR

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)

let {resourceTypeToComponentMap} = config
console.log('config', config, 'resourceTypeToComponentMap', resourceTypeToComponentMap)
if (resourceTypeToComponentMap === undefined) {
resourceTypeToComponentMap = generateResourceTypeMap(allRoutes)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It seems like callback functions are not supported values for the config/default.js. Config fields with a callback function as the value get passed as undefined.

As a temporary workaround, I've copied the config code to a generateResourceTypeMap util function in the sdk.

Comment on lines 78 to 81
// TODO: How to call urlMapping API when React Hook "useUrlMapping"
// cannot be called in a class component. React Hooks must be called
// in a React function component or a custom React Hook function.
const mapping = await getUrlMapping(path)
Copy link
Collaborator Author

@hajinsuha1 hajinsuha1 Mar 4, 2025

Choose a reason for hiding this comment

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

@bendvc I think you briefly mentioned this but the useUrlMapping hook from commerce-sdk-react can't be called here because it needs to be used within a React hook or component.

For now instead of making an actual API call I made a mock function getUrlMapping that returns the response for the following types of urlMappings: product, category, redirect, content_asset

Copy link
Collaborator

Choose a reason for hiding this comment

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

Please reach out the extensibility channel so we can start brain storming some idea's to solve this problem. I think this is potentially a blocker to getting this implemented.

Comment on lines 135 to 163
// Await the async getRoutes function
let routes = await getRoutes(locals)

let serializedRoutes = window.__CONFIG__.app.routes
// Deserialize routes
serializedRoutes = serializedRoutes.map(
({path, componentName, componentProps}) => {
let component = routes.find((route) =>
route.component?.displayName?.includes(componentName)
)?.component
if (!component) {
// TODO: Error handling if given component couldn't be found
console.error('Component', componentName, 'could not be deserialized for path', path)
return
}

if (componentProps) {
const Component = component
component = () => <Component {...componentProps} />
}
return {
path,
exact: true,
component
}
}
)
serializedRoutes = serializedRoutes.filter((route) => !!route)
routes = serializedRoutes
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@bendvc Moved the deserialization to here so it only runs on client-side

Copy link
Collaborator

Choose a reason for hiding this comment

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

Interesting.. would the async logic for making your seo url mapping called be called now on the client even though you have the routes serialized.

E.g. on line 135 you get the routes which will call the extendRoutes for all extensions and await on it.. then you will deserialize the routes starting at line 138. Effectively coming up with the same route list?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point it does seem unnecessary to deserialize routes that are already in the routes array.

I'll look to only serialize/deserialize the route of the current path

Comment on lines 29 to 33
// TODO: support "content_asset" resource types
Component = component
props = {
[`${urlMapping.resourceType}Id`]: urlMapping.resourceId
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@bendvc still need to add support for mapping content_asset resource types to a component

Comment on lines 38 to 39
// DEVELOPER NOTE: Here we would want to use a Loadable component as to not bloat the home page chunk size.
component: Component,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@bendvc this note was here in the spike but do we still want to wrap this with a Loadable component?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Are you planning on updating the README in this ticket or splitting it out into it's own?

Copy link
Collaborator Author

@hajinsuha1 hajinsuha1 Mar 5, 2025

Choose a reason for hiding this comment

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

I'm thinking it'll be better to be on it's own so that we can write the README after we have all the changes for this extension implemented

Comment on lines +11 to +15
type ResourceTypeToComponentMap = {
category: string
product: string
content_asset: string
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

We might want to leverage the enum that the API uses or the response type and gave those values.. we know that the key might be an entry of the enum = CATEGORY | PRODUCT | CONTENT_ASSET which would hep clean up this type.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We also still have the problem where we are mapping to the components display name. So we'll want to harden that solution a little. Maybe the pages in the storefront and store locator need to be prefixed with the extensions name/id to help prevent collisions or duplicate component/page names which might cause this solution to fall flat.

mapping.resourceType as keyof typeof resourceTypeToComponentMap
]
// TODO error handling if component not found
const component = routes.find((route) =>
Copy link
Collaborator

Choose a reason for hiding this comment

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

One of the caveats of using extendRoutes, and I know that I suggested that is the api method we use for this, is that "routes" is only a list of routes from the extensions that have been applied to the application before it.

Customers will have to know this via documentation (readme) that they have to add the seo mapping extension after all the extension, which might be weird. So lets put that in a decisions or findings section in your PR

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let go with the patter that each type has it's own file and the types file name and type name are synced. So we should call this serializedRoute.ts

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

after I moved the serialization/deserialization to pwa-kit-react-sdk this type is no longer in use so i've deleted it!

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it will be wise to break out the reach sdk changes for serialization of the routes into it's own PR and then we can easily share that with more pwa-kit team members.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved to #2300

Comment on lines 413 to 415
for (const applicationExtension of applicationExtensions) {
_routes = await applicationExtension.extendRoutes(_routes, req)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will this code run the promises in parallel or one after another?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This code will run one after another

@hajinsuha1 hajinsuha1 changed the base branch from feature/extensibility-v2 to route-serialization-to-support-shopper-seo-integration March 5, 2025 18:45
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.

2 participants