Skip to content

feat(audit-policy): implement audit-scope/summary E5 (B7 / SITES-47089) - #3032

Open
sandsinh wants to merge 1 commit into
mainfrom
feat/sites-47089-audit-scope-summary
Open

feat(audit-policy): implement audit-scope/summary E5 (B7 / SITES-47089)#3032
sandsinh wants to merge 1 commit into
mainfrom
feat/sites-47089-audit-scope-summary

Conversation

@sandsinh

Copy link
Copy Markdown
Contributor

Please ensure your pull request adheres to the following guidelines:

  • make sure to link the related issues in this description. Or if there's no issue created, make sure you
    describe here the problem you're solving.
  • when merging / squashing, make sure the fixed issue references are visible in the commits, for easy compilation of release notes

If the PR is changing the API specification:

  • make sure you add a "Not implemented yet" note the endpoint description, if the implementation is not ready
    yet. Ideally, return a 501 status code with a message explaining the feature is not implemented yet.
    (getScopeSections / E6 stays a 501 stub — out of scope here, audit-scope-sections path unchanged.)
  • make sure you add at least one example of the request and response.

If the PR is changing the API implementation or an entity exposed through the API:

  • make sure you update the API specification and the examples to reflect the changes.

If the PR is introducing a new audit type: N/A

Related Issues

SITES-47089 — [Spec] B7 — scope observability + section aggregation APIs (R8/R14/R15)
(sub-task of SITES-44768, Audit Policy epic). This PR covers R8 only (E5,
audit-scope/summary). R14/E6 (audit-scope/sections) stays 501, tracked separately
under the same ticket.

Companion data-layer PR (must land first / together):
adobe/mysticat-data-service#899 — adds v_audit_scope_summary, the view this endpoint reads.

Summary

Replaces the 501 stub for GET /sites/{siteId}/audit-scope/summary with a real
implementation: R8 reconciliation counts over page_inventory.

Request

GET /sites/7b2e3f9c-0000-4000-8000-000000000001/audit-scope/summary

No query parameters, siteId path param only (same shared siteId parameter every
other endpoint in this file uses).

Response (200)

{
  "inList": 1400,
  "audited": 950,
  "excluded": 120,
  "lifecycleSuppressed": 80,
  "keptStaleDetection": 5,
  "notYetScanned": 245,
  "budget": 5000,
  "strategyName": "tiered"
}
// inList = audited + excluded + lifecycleSuppressed + keptStaleDetection + notYetScanned

On a site with no rows/policy yet: { inList: 0, audited: 0, excluded: 0, lifecycleSuppressed: 0, keptStaleDetection: 0, notYetScanned: 0, budget: 5000, strategyName: "tiered" } (AuditScopeSummaryDto.defaultDocument(), same convention as
AuditPolicyDto.defaultDocument()).

This example is also now inline in docs/openapi/audit-policy-api.yaml under the 200
response, per the template requirement above.

Key decision carried over from the companion PR

Three of the five buckets in the response depend on blocks that don't exist yet: B5
(exclusion application, writes page_inventory.excluded_reason), B4 v1.1 (AEM-only
cut/staleness guard, populates cms_type), B6 (lifecycle transitions). Considered building
the exclusion-glob matcher (D7) in this cycle so excluded would show real numbers
immediately — rejected, because it would duplicate logic B5 owns and risks the summary
and the future exclusion-application logic disagreeing (the exact anti-pattern design
decision D10 calls out). Went with reading only from columns/predicates that already
exist (excluded_reason, cms_type/cms_confidence/cms_source_fresh_until) — both
buckets correctly compute to 0 today and will report real numbers automatically once
B4/B5 ship, no changes needed on this endpoint when that happens. Full detail in the
data-service PR's description.

Changes

  • src/controllers/audit-policy.jsgetScopeSummary now reads v_audit_scope_summary
    via .from().eq('site_id', ...).maybeSingle() (same shape as getPolicy/getScopePages),
    maps through the new DTO, falls back to the zeroed default document when no row exists.
    getScopeSections (E6) untouched, still 501.
  • src/dto/audit-policy.js — new AuditScopeSummaryDto (toJSON + defaultDocument),
    reuses the existing DEFAULTS const.
  • docs/openapi/audit-policy-api.yamlaudit-scope-summary path: 200 + new
    AuditScopeSummary schema + inline response example + 400/401/403/404/500
    (matching the sibling audit-scope-pages block); added a request-example note in the
    description since this is a GET with no body.
  • docs/index.html — regenerated (npm run docs:build).
  • Tests:
    • Unit: pulled getScopeSummary out of the E4-E6 scope-read 501 stubs loop (renamed to
      E6 scope-read 501 stub, only getScopeSections left in it); added a new
      E5 getScopeSummary describe block — DTO shape + identity check, site_id filter
      assertion, default-fallback when no row, 403 without querying, 500 + log on PostgREST
      error. 78/78 unit tests passing.
    • Integration: split API-15 so only /audit-scope/sections asserts 501; added a
      describe.skip'd block for /audit-scope/summary (API-13 per the contract doc's
      test matrix) — skipped because the pinned mysticat-data-service image in
      test/it/postgres/docker-compose.yml (v5.90.0) predates the view added in the
      companion PR. Un-skip once that pin bumps to a release containing
      20260811193840_audit_scope_summary_view.sql — same sequencing E4
      (v_audit_scope_pages) went through.

Verification

  • npx eslint clean on all changed files
  • npx mocha test/controllers/audit-policy.test.js — 78/78 passing
  • npm run docs:lint — valid, no new warnings against this file
  • npm run docs:build — succeeds
  • Manually verified the backing view's reconciliation identity and default-fallback
    behavior against a real local Postgres (see companion data-service PR for the query
    output)

Thanks for contributing!

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

Real implementation of GET /sites/{siteId}/audit-scope/summary, replacing
the 501 stub. Reads v_audit_scope_summary (mysticat-data-service migration
20260811193840_audit_scope_summary_view.sql), maps to the R8 shape via a
new AuditScopeSummaryDto, falls back to defaultDocument() (budget 5000 /
strategyName tiered) when the site has no rows yet — same convention as
AuditPolicyDto.defaultDocument.

OpenAPI: audit-scope-summary now documents 200 + AuditScopeSummary schema
with a response example, plus 400/401/403/404/500 matching the sibling
audit-scope-pages block. getScopeSections stays a 501 stub (E6, out of
scope for this ticket).

Tests: split getScopeSummary out of the E4-E6 501-stub loop, added 5 unit
tests (DTO shape, site_id filter, default-fallback, 403, 500). IT: split
API-15 so only /audit-scope/sections asserts 501; added a describe.skip'd
block for /audit-scope/summary, gated on the mysticat-data-service Docker
image pin (test/it/postgres/docker-compose.yml, v5.90.0) catching up to a
release containing v_audit_scope_summary — same gating E4 originally used.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@sandsinh
sandsinh deployed to dev-branches August 11, 2026 20:03 — with GitHub Actions Active
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

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