MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-343

6.3.2 Report + widget data reads — created-vs-resolved buckets, distribution group-by (TOTAL statistic registry), filter-results page; per-VIEWER 6.4 gating

Done
Description

Estimate: 32m · Depends on: 4.6.3, 6.1.2, 6.2.1

The aggregation reads every widget and report page consumes — extending reportsService (the home 4.6.3 creates). Pure backend.

getCreatedVsResolved(scope, { period, daysBack, cumulative }) — scope = { projectId } | { savedFilterId } (the filter resolves through THE 6.2.1 resolve-by-id contract — decode + registry-validate via 6.1.1, never trust-and-compile — into the WHERE fragment the buckets scope to). Two series: created = createdAt bucketed by day/week/month over the days-back window; resolved = NET transitions into a done-category status (getTerminalStatusKeys — the recorded deviation: our resolution IS the done category) derived from the 1.4.6 revision trail via ONE bounded grouped $queryRaw (the 4.6.3 pattern; a reopen inside the window subtracts). cumulative: true running-sums within the window server-side. Window capped (e.g. ≤366 days, ≤120 buckets) → typed 422 beyond.

getDistribution(scope, statisticType) — a bounded GROUP-BY count over the scoped items, through a TOTAL statistic-type registry: the enum-ish field vocabulary the 6.1 registry already enumerates (kind, status, priority, assignee, reporter, sprint, label, component, select-CF cf:<id>, user-CF) — label/component/CF group-bys ride the SAME 5.3.1/5.4.1 indexed joins 6.1.2 compiles (one item counted once per label ⇒ multi-label items appear in multiple segments, the Jira behaviour — documented); unknown statistic ids → 422. Returns segments (id, label, count, percentage) + the total; a NULL group surfaces as the designed "None" segment.

getFilterResultsPage(scope, page, pageSize ≤ 50) — rides the EXISTING 2.5.8/2.5.12 list read + count with the compiled fragment (no second query path; the verified 50/page gadget cap enforced server-side).

Per-VIEWER gating (the 6.4 seam). Every read resolves access for the REQUESTING user: a project-sourced scope checks 6.4 project access; a filter-sourced scope rides the 6.2.1 resolve (already behind the 6.4 browse gate + filter visibility for the CALLER — pass the viewer, never the owner). Denied → a typed no-access result (the widget state), NEVER partial data or a leaked count. Stale referents (deleted filter/project/statistic referent) → the typed stale result (the 6.1.2 unknown-value precedent).

Acceptance criteria

  • The bucket matrix holds against seeded revisions at known dates: day/week/month × cumulative × reopens (net), window edges inclusive, created-vs-resolved reconciles with the seeded counts; the resolved derivation is ONE bounded grouped query (no all-revisions load — asserted on the large seed via query inspection + a timing sanity bound).
  • The statistic registry is total (enumeration test over every entry incl. dynamic CF entries); each group-by rides the indexed joins (EXPLAIN spot-checks); percentages sum to 100±rounding; the None segment + multi-label multi-count behaviours are asserted.
  • Filter-results pages exactly match the /issues list for the same filter (parity test) at ≤50/page.
  • Per-viewer gating: the no-access matrix (private project × workspace-shared dashboard × viewer/non-viewer) is asserted route-level; stale referents → typed stale results; window/bucket caps → 422.
  • Routes HTTP-only; empty-input guards on new repo aggregates; pnpm test:coverage ≥90%.

Context refs

  • 4.6.3 reportsService + workItemRevisionRepository.aggregate* (the bounded grouped-derivation pattern + the done-category predicate via getTerminalStatusKeys)
  • 6.1.1/6.1.2 (the compiled WHERE fragment + the 5.3.1/5.4.1 join contracts the group-bys reuse); 6.2.1 (the resolve-by-id data-source contract — the service JSDoc written for this story)
  • 6.4 project-access service (the per-viewer gate); finding #26
  • The verified Jira report configs in the Story 6.3 description; findings #57 (bounded), #29 (total registries)
Status
Done
Type
Sub-task