Skip to content

feat: add path-level prerender deploy support to tokowaka-client#1598

Open
ssilare-adobe wants to merge 4 commits into
mainfrom
feat/path-level-prerender-suggestions
Open

feat: add path-level prerender deploy support to tokowaka-client#1598
ssilare-adobe wants to merge 4 commits into
mainfrom
feat/path-level-prerender-suggestions

Conversation

@ssilare-adobe
Copy link
Copy Markdown
Contributor

@ssilare-adobe ssilare-adobe commented May 11, 2026

Context

This is PR 1 of 4 in the end-to-end path-level prerender suggestions feature. It must ship and be consumed by `spacecat-api-service` before path deploy requests can be made.

Feature overview: The prerender audit today generates per-URL and domain-wide suggestions. This feature adds a third tier — path-level suggestions (e.g., `/products/*`) — that group URLs by their first path segment. When a path is deployed, the CDN allow-list is updated with that path pattern (append semantics), and per-URL suggestions under that prefix are marked as covered. See the full design plan for details.

Deployment sequence

# Repo Status
1 spacecat-shared (tokowaka-client) ← this PR Must ship first
2 spacecat-api-service Depends on this PR
3 spacecat-audit-worker Can develop in parallel, deploy after #1
4 project-elmo-ui Deploy last

What this PR does

Extends packages/spacecat-shared-tokowaka-client to handle pathType: true suggestions in the deployToEdge and rollbackSuggestions methods.

Key changes

deployToEdge — path suggestions use append semantics:

  • Classify suggestions with data.pathType === true as pathSuggestions (separate from domainWideSuggestions and per-URL validSuggestions)
  • Read existing metaconfig.prerender.allowList → append the new path pattern (deduped via Set) → write back
  • Never generates per-URL S3 configs for path suggestions
  • Sets edgeDeployed timestamp on the suggestion after successful deploy
  • Returns coveredSuggestions for per-URL suggestions whose URL starts with the deployed path prefix

rollbackSuggestions — path and domain-wide suggestions use remove-one semantics:

  • Path (pathType: true) and domain-wide (isDomainWide: true) suggestions are classified before filterEligibleSuggestions (the prerender mapper's canDeploy rejects them since they have no url field)
  • Each is rolled back by fetching the metaconfig, removing its allowedRegexPatterns[0] from allowList, and writing back
  • If allowList is empty after removal, deletes the prerender key entirely
  • Does NOT touch other patterns (e.g., leaves /* intact when rolling back /products/*)
  • Errors per-suggestion are collected and returned in failedSuggestions; other suggestions in the same batch are unaffected

Domain-wide deploy updated (no longer full-replace):

  • Domain-wide deploy now merges /* into the existing allowList instead of full-replacing
  • This preserves already-deployed path patterns when domain-wide is deployed on top
  • Rollback of domain-wide removes only /* from the list via the same remove-one path

Concurrent path deploys (race condition mitigation):

  • When multiple path suggestions are deployed in the same batch (e.g., user multi-selects), process them sequentially per baseUrl rather than in parallel to avoid last-writer-wins on the allowList

allowList behaviour summary

Action Domain-wide (/*) Path type (/products/*)
Deploy Merge /* into existing list Append only — reads existing list, adds new pattern, writes back
Rollback Remove /*, delete key if empty Remove one pattern, delete key if list empty after removal

Test additions (packages/spacecat-shared-tokowaka-client/test/index.test.js)

  • Path deploy appends /products/* when allowList is empty → ['/products/*']
  • Path deploy appends /products/* when allowList: ['/*']['/*', '/products/*'] (domain-wide preserved)
  • Path deploy is a no-op if the pattern is already in allowList
  • No per-URL S3 config generated for path suggestions
  • Path rollback of /products/* removes only that pattern, leaves /* intact
  • Path rollback of the last remaining pattern deletes prerender key entirely
  • Domain-wide deploy merges /* into existing list (not full replace)
  • Domain-wide rollback removes only /*
  • coveredSuggestions returns matching per-URL suggestions for path deploys
  • Sequential processing of multiple path suggestions in one batch
  • Path rollback skips CDN write when metaconfig has no prerender key
  • Path rollback skips CDN write when pattern is not in allowList
  • Path rollback marks suggestion ineligible when allowedRegexPatterns is missing
  • Path rollback marks suggestion ineligible when no metaconfig exists
  • Path rollback marks suggestion as failed with statusCode: 500 when upload throws

Test plan

  • Run npm test -w packages/spacecat-shared-tokowaka-client — all 718 tests pass, 100% line/branch/statement coverage
  • New path deploy/rollback tests pass
  • Domain-wide deploy/rollback regression tests pass

🤖 Generated with Claude Code

@github-actions
Copy link
Copy Markdown

This PR will trigger a minor release when merged.

}

/** Returns true if the suggestion is a path-level prerender suggestion. */
function isPathSuggestion(suggestion) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lets use coveredByPattern prop in the schema for the segment related suggestions.
isCoveredByPathPattern should be a generic method that returns true if suggestion.getData()?.coveredByPattern || suggestion.getData()?.isDomainWide

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@nit23uec This checks if the suggestion is a path level suggestion and not if the suggestion is covered by path level suggestion.

What this function checks:
https://adobe.com/products/* -> Path level suggestion -> returns true

If we need to add coveredByPattern, it should come on the suggestions which are covered by pattern like https://adobe.com/products/test rather than on path level suggestion.

// Validate which suggestions can be rolled back
// Separate metaconfig-based suggestions (path and domain-wide allowList entries)
// from per-URL suggestions — each group is rolled back via a different code path.
const metaconfigSuggestions = suggestions.filter((s) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nitpick: metaconfig is an impl detail.
lets use patternCoveredSuggestions to filter segment and domain wide suggestions


try {
// eslint-disable-next-line no-await-in-loop
const metaconfig = await this.fetchMetaconfig(baseURL);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

earlier also, we used to support a domain wide rollback - can you pls make sure we are usig the same code to rollback such suggestions. Pls sync with @dipratap


targetSuggestions.forEach((suggestion) => {
const data = suggestion.getData();
if (data?.isDomainWide === true) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

better to reuse the generic method coveredByPattern proposed above to do any handling of such suggestions.

if (Array.isArray(allowedRegexPatterns) && allowedRegexPatterns.length > 0) {
domainWideSuggestions.push({ suggestion, allowedRegexPatterns });
}
} else if (isPathSuggestion(suggestion)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

domainwide and pathbased should be handled exactly on a similar basis. there should be no separate if else required for them.
we did a mistake when we introduced domainWide prop for the /* suggestions - we should have just introduced a generic prop coveredByPattern to cover any regex case.

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