fix(analytics): read tag data for sites deco doesn't host

Merged
TH
8d ago
+121-32

What is this contribution about?

A store on another commerce platform can get Deco Analytics through ANALYTICS_INSTALL: the site registers with the collector by key, and the project stores that registration in metadata.decoAnalytics. Only the server writes this key; stripServerManagedMetadata drops it from client writes.

Those sites are never in org_sites. So GET /api/:org/hosting/:site/analytics/data answers 404 for them, and the org Analytics board, which reads tag pageviews through that route, drops the site. An installed, collecting store shows up nowhere in Studio.

The data route now reads the stored registration's warehouse id when the org doesn't own the slug:

  • Owned slug: no change. The route still resolves the id through the control-plane.
  • Unowned slug: the route finds this org's project for the slug (findOwnedProject) and uses its decoAnalytics.id. The id still comes from the server, never from the client. It was created by this org's install, which refuses a domain someone else already registered. Without a stored registration, the route still answers 404.
  • Control-plane settings: only the owned path requires them now. The external path never calls the control-plane.
  • Tenant check: the payload check (usageScope.site === siteId) still applies to both paths.

The parser for the stored registration moves from the install tool to analytics/registration.ts. The install tool and the route now read it the same way.

Out of scope: the project's own Analytics view. Its presence rule and tab still only cover hosted sites, and adding external sites there is a separate change.

How did you verify your code works?

  • bun test src/analytics/registration.test.ts src/api/routes/hosting.test.ts: 9 pass. That includes the new storedExternalRegistration case: a valid registration, null metadata, a missing key, an empty id and a missing key field.
  • bun run fmt and bun run lint are clean.
  • bun run knip fails only on an untracked local scratch file that isn't part of this branch.
  • bun run test: 12757 pass / 80 fail / 6 errors. The base revision on the same machine, with this diff stashed, gives 12756 / 80 / 6, so the failures predate this change. They come from local sandbox and protocol suites with a stale @decocms/blocks install, not from analytics.
  • tsc --noEmit in apps/api reports no errors in the changed files.
  • Not verified yet: the analytics read surface answering for a key-registered (d-…) id. Checking it needs this deployed: the org Analytics board should list the external store with tag pageviews.

How to Test

  1. In an org with a project for a store not hosted by deco, run ANALYTICS_INSTALL with the store's domain. Merge the generated change and deploy it.
  2. Open the org Analytics board.
  3. Expected: GET /api/<org>/hosting/<site>/analytics/data?view=overview&range=7d answers 200 with registered: true, and the store appears with tag pageviews. Before this change, the route answered 404.
  4. For a slug the org neither owns nor registered, the route still answers 404.

Review Checklist

  • PR title is clear and descriptive
  • Changes are tested and working
  • Documentation is updated (if needed)
  • No breaking changes

šŸ¤– Generated with Claude Code


Summary by cubic

Makes the Analytics data route answer for sites registered through ANALYTICS_INSTALL that deco doesn't host, so their tag pageviews show up on the org Analytics board instead of dropping out.

When the org doesn't own the site slug, the route now reads the warehouse id from the stored registration in the project's metadata instead of answering 404. Owned sites keep the existing control-plane resolution path, and the control-plane is no longer required for the external path. The stored-registration parser moves from the install tool to analytics/registration.ts so both use the same validation.

Behavioral notes

  • Storage failures during the external registration lookup now log and answer 502; previously they were misreported as 404 "not owned".
  • A site the org owns or registered still requires a valid usageScope.site match in the payload.
  • A slug the org neither owns nor registered still answers 404.
  • Resolving the external id needs the registration to be complete; a missing or empty decoAnalytics.id falls back to 404.