Skip to content

feat: auto-create product entitlement and site enrollment via x-product header#2432

Open
radhikagpt1208 wants to merge 2 commits into
mainfrom
SITES-44944
Open

feat: auto-create product entitlement and site enrollment via x-product header#2432
radhikagpt1208 wants to merge 2 commits into
mainfrom
SITES-44944

Conversation

@radhikagpt1208
Copy link
Copy Markdown
Contributor

@radhikagpt1208 radhikagpt1208 commented May 18, 2026

Summary

Adds an opt-in x-product header to POST /sites and POST /organizations that triggers auto-creation of the org's product entitlement (FREE_TRIAL tier) and — for sites — the corresponding SiteEnrollment, by delegating to TierClient (which is idempotent at the library level: existing entitlements/enrollments are reused).

Without the header the endpoints behave exactly as before, preserving backward compatibility for callers that manage entitlements separately.

Jira: SITES-44944

Problem

GET /organizations/:orgId/sites filters results through filterSitesForProductCode(), which keeps only sites that have a SiteEnrollment row for the org's Entitlement:

Site.allByOrganizationId(orgId)
  └─ filterSitesForProductCode(sites)
      ├─ TierClient.checkValidEntitlement(org, productCode)
      ├─ SiteEnrollment.allByEntitlementId(entitlementId)
      └─ sites.filter(s => enrolledSiteIds.has(s.getId()))

Sites onboarded through the aem-aso-trial flow are persisted in Spacecat but never get an Entitlement / SiteEnrollment, so they get filtered out of the product-scoped listing. The downstream effect: ASO Preflight breaks ("site is not supported for this widget") for trial customers, blocking the technical-validation flow used in customer workshops.

This was previously masked by loose (env id + program) matching that picked the first matching site — effectively a bug, surfaced once provisioning was tightened. The team had been working around it with manual set imsorg Slack admin commands per site.

Once the trial flow (aem-aso-trial) passes x-product: ASO on POST /sites (and POST /organizations), new trial sites are enrolled out-of-the-box and Preflight works without any manual provisioning step.

Original incident thread: Preflight broken for AEM Sites Trial.

Changes

src/controllers/sites.js

  • New helper ensureSiteEntitlementAndEnrollment that calls TierClient.createForSite(...).createEntitlement(FREE_TRIAL).
  • createSite reads x-product from headers and invokes the helper after the site is persisted (for both newly created and existing site paths). Status code logic hoisted into a let status variable so the entitlement step runs uniformly while preserving the existing 201/200 idempotent-create semantics.
  • Entitlement/enrollment failures return 500 with a descriptive message (Failed to ensure <PRODUCT> entitlement/enrollment for site); retries are safe because TierClient is idempotent and the site lookup on retry returns the already-persisted site.

src/controllers/organizations.js

  • New helper ensureOrgEntitlement that calls TierClient.createForOrg(...).createEntitlement(FREE_TRIAL).
  • createOrganization reads x-product from headers and invokes the helper, with the same idempotent-create semantics (200 for existing, 201 for newly created).
  • Adds internalServerError import from @adobe/spacecat-shared-http-utils.

OpenAPI specs

  • Adds reusable xProduct parameter under docs/openapi/parameters.yaml.
  • Updates sites-api.yaml and organizations-api.yaml POST endpoints to reference the new parameter and document the auto-enrollment behavior + failure modes.
  • Adds the missing 200 response on POST /organizations for the idempotent-existing case.

Tests

  • New describe blocks createSite auto-enrollment via x-product header (6 cases) and createOrganization auto-entitlement via x-product header (5 cases) covering: success on newly created, success on existing, header missing, empty-string header, TierClient failure on newly created, TierClient failure on existing.

Backward compatibility

Without the x-product header, both endpoints behave exactly as before — no entitlement/enrollment is created and no TierClient call is made. Existing integrations (PLG onboarding, Slack onboard site command, etc.) that manage enrollment via their own paths are unaffected.

Test plan

  • npm test (mocha + c8): all new and existing tests pass; 100% branch coverage on the changed code.
  • eslint on changed files — clean.
  • npm run docs:lint — clean (only pre-existing warnings in examples.yaml).
  • npm run docs:build — succeeds.
  • Manual: POST /sites with x-product: ASO on stage → site appears in GET /organizations/:orgId/sites for the same product code.
  • Manual: POST /sites without x-product → behavior unchanged.

Follow-up

A companion PR in aem-aso-trial will wire up x-product: ASO on its POST /sites and POST /organizations calls so trial sites are enrolled out-of-the-box. Tracked under SITES-44944.

References

Radhika Gupta and others added 2 commits May 18, 2026 19:18
Regenerate the lockfile after main was merged in: the previous lockfile
referenced older versions of @adobe/spacecat-shared-* and @aws-sdk/*
packages and was missing xml-naming@0.1.0, causing `npm ci` to fail in
CI with EUSAGE ("lock file's X@a does not satisfy X@b").

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions
Copy link
Copy Markdown

This PR will trigger a minor release when merged.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 18, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

1 participant