Implement Supabase view query wrappers
epic-bufdir-data-aggregation-data-layer-task-004 — Add typed query builder methods for Supabase view-based queries (as opposed to RPC calls): raw activity records, raw event records, and raw contact records for a given org and date range. Apply org-isolation filters, support pagination where needed, and return typed list results. These methods complement the RPC wrappers for scenarios where view queries are more appropriate than RPCs.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Implement the three methods as public async methods on `AggregationQueryBuilder` alongside the existing RPC wrappers. Use a private helper `_queryView
For large datasets in production, callers should loop with incrementing offsets until a returned list is shorter than the page size — document this pattern in the method's doc comment. These view wrappers are complementary to RPC wrappers: use RPCs for pre-aggregated counts and views for raw record access needed by the deduplication chain.
Testing Requirements
Unit tests using `flutter_test` and mocktail must cover: (1) Happy-path for each of the three methods: stub the Supabase query chain mock to return a valid list of JSON maps; assert the returned Dart list matches the expected deserialized models. (2) View name correctness: verify the exact Supabase view name string is passed to `supabase.from()` for each method. (3) Org filter application: verify the `organization_id` equality filter is applied before date range filters. (4) Date range filters: verify `.gte()` and `.lte()` are called with the correct column names and ISO 8601 date strings.
(5) Pagination: verify `.range(0, 499)` is called by default and that custom offset/limit values are correctly forwarded. (6) Empty result: stub the mock to return an empty list; assert the method returns `[]` without throwing. (7) Error handling: stub the mock to throw a `PostgrestException`; assert `AggregationQueryException` is thrown with the view name included. Test coverage target: 100% of all three method branches.
Supabase RPC functions return JSON with PostgreSQL numeric types (bigint, numeric) that do not map cleanly to Dart int/double. Silent truncation or JSON parsing errors could corrupt participant counts in the final Bufdir submission without any runtime exception.
Mitigation & Contingency
Mitigation: Define explicit Dart fromJson factories for all RPC result models with type-safe parsing and assertion checks. Add a contract test that compares raw RPC JSON output against expected Dart model values using a known seed dataset.
Contingency: If type mismatches are found in production metrics, expose a validation endpoint in BufdirMetricsRepository that re-fetches and compares raw RPC output against the persisted snapshot, flagging any discrepancies before export proceeds.
Persisted metric snapshots can become stale if additional activities are registered after the snapshot is saved but before the export is finalized. Coordinators might unknowingly export data that does not reflect the latest activity registrations.
Mitigation & Contingency
Mitigation: Store a snapshot_generated_at timestamp and a record_count_at_generation field in the snapshot. When the coordinator views cached results, compare the current activity count for the period against the snapshot value and display a 'Data updated since last aggregation — re-run?' warning if counts differ.
Contingency: Add a mandatory staleness check before the export confirmation dialog can proceed: if the snapshot is more than 24 hours old or the record count has changed, require the coordinator to re-run aggregation before the export button is enabled.