Skip to content

feat(http-utils): add composite primary-resource resolver hook to facsWrapper - #1883

Open
ravverma wants to merge 2 commits into
mainfrom
feat/facs-composite-resource-key
Open

feat(http-utils): add composite primary-resource resolver hook to facsWrapper#1883
ravverma wants to merge 2 commits into
mainfrom
feat/facs-composite-resource-key

Conversation

@ravverma

Copy link
Copy Markdown
Contributor

What (PR 2 of 3)

Adds a composite primary-resource resolver hook to facsWrapper so a product can scope its PRIMARY resource by an extra qualifier (ASO's (site × opportunity-type)) — while the shared wrapper stays product/route-agnostic.

Design: ReBAC Composite Resource Key ADR — adobe/mysticat-architecture#249 (rebac-composite-resource-key.md, D3/D4).

Change

  • New opt-in config PRODUCTS_FACS_COMPOSITE_RESOURCE + a compositeResolvers registry (construction validation mirrors secondaryResolvers).
  • On the primary path, a composite-keyed product delegates the grant decision to its registered resolver, which returns a tri-state:
    • true → grant
    • 'defer' → set context.attributes.facs + defer to the controller (collection routes that ReBAC-filter, D4)
    • anything else → deny (403); a thrown error fails closed
  • The JWT org-wide short-circuit still runs first; non-composite products/resource-types are unchanged.
  • .d.ts updated (FacsCompositeResolver type + config + opts).

All ASO specifics ('all' short-circuit, Opportunity→type fetch, collection defer) live in the api-service resolver (PR 3) — this hook is inert until a service registers a resolver, so it's a no-op for existing consumers.

Tests

Lint clean; 504 passing; facs-wrapper.js at 100% stmts/branch/funcs/lines (package 100%/99.18%, above the 97% branch gate). 7 new composite tests: creation guards + grant / defer / deny / throw (fail-closed) / JWT short-circuit / resource-type-mismatch fall-through.

Sequence: (1) mysticat-data-service migration (#923) → (2) this → (3) spacecat-api-service write/collection/OpenAPI.

🤖 Generated with Claude Code

…sWrapper

Adds an opt-in PRODUCTS_FACS_COMPOSITE_RESOURCE config + compositeResolvers registry (mirroring secondaryResolvers). On the primary path, a composite-keyed product delegates the grant decision to its registered resolver, returning a tri-state: true=grant, 'defer'=set context.attributes.facs + defer to the controller (collection routes that ReBAC-filter), else deny; fail-closed on throw. Keeps the shared wrapper product/route-agnostic — all qualifier logic lives in the consuming service's resolver. Inert until a service registers one. Design: adobe/mysticat-architecture#249 (rebac-composite-resource-key.md D3/D4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

This PR will trigger a minor release when merged.

@ravverma
ravverma requested a review from MysticatBot August 24, 2026 05:15

@MysticatBot MysticatBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey @ravverma,

Verdict: Approve - well-structured opt-in hook with correct fail-closed semantics and thorough test coverage.
Complexity: MEDIUM - medium diff; ReBAC + auth signal.
Changes: Adds an opt-in composite primary-resource resolver hook to facsWrapper, enabling products to scope their primary resource by an extra qualifier with tri-state grant/defer/deny semantics (3 files).

Non-blocking (3): minor issues and suggestions
  • nit: Missing org field in the composite-resolver-error log entry - the grant and deny logs include org: normalizedOrgId but the error/fail-closed catch block omits it, reducing log correlation on resolver failures - packages/spacecat-shared-http-utils/src/auth/facs-wrapper.js:652
  • suggestion: The "defer" test should assert the resolver args shape (specifically that routeParams is { siteId: 'site-abc' } with no opportunityId on the collection route) to match the thoroughness of the grant test - packages/spacecat-shared-http-utils/test/auth/facs-wrapper.test.js:1213
  • nit: Code comments reference platform/decisions/rebac-composite-resource-key.md which resolves to companion PR adobe/mysticat-architecture#249 (not yet merged to main) - the path will be valid once that ADR lands, but is currently a dead link for readers checking the architecture repo - packages/spacecat-shared-http-utils/src/auth/facs-wrapper.js:256

Skill: pr-review | Model: us.anthropic.claude-opus-4-6-v1[1m] | Duration: 1m 40s | Cost: $6.83 | Commit: 29af8f2b747c728b10d3a5c4cf434ec87482e801
If this code review was useful, please react with 👍. Otherwise, react with 👎.

@MysticatBot MysticatBot added ai-reviewed Reviewed by AI complexity:medium AI-assessed PR complexity: MEDIUM labels Aug 24, 2026
ravverma added a commit to adobe/spacecat-api-service that referenced this pull request Aug 24, 2026
…ring

Adds src/support/facs-composite-resolvers.js (asoOpportunityComposite), registered as compositeResolvers and wired into facsWrapper (src/index.js), plus PRODUCTS_FACS_COMPOSITE_RESOURCE.ASO in facs-capabilities.js. The resolver classifies ASO site routes: opportunity item -> 'all' OR opp.type (fail-closed on unresolved opp / site-mismatch); opportunity list -> defer (controller filters); every other ASO site route -> any active site binding with the capability. Inert until http-utils (adobe/spacecat-shared#1883) releases + is bumped. Design: adobe/mysticat-architecture#249 (D3/D4). Follow-ups: write path (D6), collection filter (D4), OpenAPI, IT tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-reviewed Reviewed by AI complexity:medium AI-assessed PR complexity: MEDIUM

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants