Skip to content

@W-17543649 - [SEO] Before calling getUrlMapping check the React Router Config first#2379

Merged
yunakim714 merged 48 commits intouseBlockfrom
W-17543649-seo-allowlist-routes
May 14, 2025
Merged

@W-17543649 - [SEO] Before calling getUrlMapping check the React Router Config first#2379
yunakim714 merged 48 commits intouseBlockfrom
W-17543649-seo-allowlist-routes

Conversation

@yunakim714
Copy link
Collaborator

@yunakim714 yunakim714 commented Apr 29, 2025

Description

In the current SEO implementation, the getUrlMapping API is called on every navigation. However, we want to offer customers an opt-in functionality to "allowlist" the routes predefined in code.

This PR enables just that - if users set their routingMode to be router_first, then the extension will check whether the given URL path has been predefined in code. If the path has been defined, then we route the customer there without calling out to the API. If routingMode is api_first, then the extension will call out to the API on every navigation.

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 isRouteDefined() method which checks whether the given URL path matches a route predefined in the application's routes config (excluding routes that end in a catch-all, e.g. path='*', path='RefArch*')
  • Add configuration value routingMode to the extension config.
    • If routingMode === 'router_first' : Check if location.pathname matches a predefined route and skip the getUrlMapping API call
    • If routingMode === 'api_first' : Always call the API
  • In seo-hoc.tsx, check the config value against the new enum, RoutingMode

If routingMode === 'router_first':

  1. Click link
  2. Block
    a. If original path exists, route there <- This is the newly introduced check
    b. If not, call getUrlMapping
    i. If there is a valid response, navigate there
    ii. If no response, show 404
  3. Unblock

If routingMode === 'api_first':

  1. Click link
  2. Block
  3. Call getURLMapping:
    a. If there is a valid response, navigate there
    b. If no response:
    i. If original path exists, route there
    ii. If none, then show 404
  4. Unblock

How to Test-Drive This PR

Checking router_first behavior

  • Checkout this branch -> Navigate to template-typescript-minimal package -> In the package.json, the config should be set to routingMode === 'router_first'.
  • Navigate to Womens -> Clothing. Verify that you see the women's clothing PLP and the path remains category/womens-clothing. The API is not being called because there is a route predefined.

Checking api_first behavior

  • Checkout this branch -> Navigate to template-typescript-minimal package -> In the package.json, Set the config to routingMode === 'api_first'.
  • Navigate to Womens -> Clothing. Verify that the storefront redirects to category/mens. The API is being called.

To run unit tests

  • Run npm run test from the extension-commerce-bm-seo package root

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)

@cc-prodsec
Copy link
Collaborator

cc-prodsec commented Apr 29, 2025

🎉 Snyk checks have passed. No issues have been found so far.

security/snyk check is complete. No issues have been found. (View Details)

license/snyk check is complete. No issues have been found. (View Details)

@yunakim714 yunakim714 changed the base branch from extensibility/seo-feature to useBlock April 29, 2025 16:00
Signed-off-by: Yuna Kim <84923642+yunakim714@users.noreply.github.com>
@yunakim714 yunakim714 changed the title @W-17543649 - [SEO] Read matchingStrategy from config @W-17543649 - [SEO] Read matchingStrategy from config and check "cache" on CACHE_FIRST Apr 29, 2025
@yunakim714 yunakim714 added the skip changelog Skip the "Changelog Check" GitHub Actions step even if the Changelog.md files are not updated label Apr 29, 2025
@yunakim714 yunakim714 requested a review from bendvc April 29, 2025 18:54
@yunakim714 yunakim714 marked this pull request as ready for review April 30, 2025 17:52
@yunakim714 yunakim714 requested a review from a team as a code owner April 30, 2025 17:52
@bendvc
Copy link
Contributor

bendvc commented Apr 30, 2025

This is a good time to start adding to the README to start filling out the "configuration" section. In this readme we should also explain that if they choose the "cache first" mode, that they will never be able to control those routes in BM, that is a pretty important limitation that we'll have to communicate with the users.

// `matchingStrategy == CACHE_FIRST`: if `location.pathname` matches a predefined route, skip the `getUrlMapping` API call
// `matchingStrategy == API_FIRST`: always call `getUrlMapping`
const skipMappingCall =
matchingStrategy === 'CACHE_FIRST' && isRouteDefined(location.pathname, routes)
Copy link
Contributor

Choose a reason for hiding this comment

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

We can probably go ahead and create an enum and a type for the matching strategy configuration so we aren't comparing with a static string here.

Copy link
Contributor

@bendvc bendvc left a comment

Choose a reason for hiding this comment

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

Overall nice work. I've left some comments to make it a little better.

it('should call the callback and push navigation after callback resolves', async () => {
const callback = jest.fn().mockResolvedValue(undefined)
render(<TestComponent callback={callback} />)
const {unmount} = render(<TestComponent callback={callback} />)
Copy link
Contributor

Choose a reason for hiding this comment

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

Was this just a linting thing?

@yunakim714 yunakim714 merged commit 1115b79 into useBlock May 14, 2025
4 of 33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip changelog Skip the "Changelog Check" GitHub Actions step even if the Changelog.md files are not updated

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants