Implement CertificationRepository CRUD methods
epic-certification-management-foundation-task-003 — Build the CertificationRepository Dart class with full CRUD: createCertification, getCertificationById, getCertificationsByMentorId, updateCertification, softDeleteCertification. All methods must use the Supabase client with proper RLS context. Repository interface must be defined as an abstract class to support mocking in tests.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
Define CertificationRepository as an abstract class in lib/features/certification/data/repositories/certification_repository.dart. Place the Supabase implementation in lib/features/certification/data/repositories/supabase_certification_repository.dart. Use freezed for CertificationModel to get copyWith, equality, and json support for free. For the renewal_history JSONB, define a RenewalHistoryEntry freezed class and serialize the list with json_serializable.
Register SupabaseCertificationRepository as a Riverpod Provider so it can be overridden with MockCertificationRepository in tests. Do not use .execute() pattern from older Supabase SDK versions; use the modern .select().eq().maybeSingle() fluent API. Wrap all Supabase calls in a try/catch converting PostgrestException to a typed RepositoryException defined in the domain layer.
Testing Requirements
Unit tests (flutter_test): mock the SupabaseClient using a hand-written or Mockito-generated mock; test each CRUD method for: (1) happy path returning correct model; (2) Supabase 404 / empty response returning null or empty list; (3) PostgrestException being re-thrown as RepositoryException; (4) softDelete setting status to 'suspended' not deleting the row. Integration tests: run against a local Supabase instance (supabase start) and verify actual database round-trips for each method. Aim for 90%+ line coverage on the repository class.
HLF Dynamics portal webhook API contract may be undocumented, subject to change, or require a separate authentication flow not yet agreed upon with HLF. If the contract changes post-implementation, the sync service silently fails and expired peer mentors remain on public listings.
Mitigation & Contingency
Mitigation: Obtain the official Dynamics webhook specification and test credentials from HLF before starting HLFDynamicsSyncService implementation. Agree on a versioned webhook contract and request a staging endpoint for integration testing.
Contingency: If the contract is unavailable, stub the sync service behind a feature flag and ship without Dynamics sync initially. Queue sync events locally and replay once the contract is confirmed.
Supabase RLS policies for certifications must correctly scope data to the coordinator's chapter without leaking cross-organisation data, particularly complex in multi-chapter membership scenarios. A misconfigured policy could expose peer mentor PII to wrong coordinators.
Mitigation & Contingency
Mitigation: Write RLS policies against the established org-hierarchy schema used by other tables. Peer review all policies before migration deployment. Add integration tests that assert cross-organisation data isolation using test accounts with different org scopes.
Contingency: If a policy gap is discovered post-merge, immediately disable the affected query endpoint and apply a hotfix migration. Audit access logs in Supabase for any cross-org data access events.
Storing renewal history as a JSONB field rather than a normalised table simplifies queries but makes retrospective schema changes (adding fields to history entries) harder and could cause issues if history grows very large for long-tenured mentors.
Mitigation & Contingency
Mitigation: Define a versioned JSONB entry schema (include a schema_version field in each entry) so future migrations can transform old entries. Add a size guard in the repository to warn if renewal_history exceeds 500 entries.
Contingency: If JSONB approach proves limiting, add a normalised certification_renewal_events table and migrate history entries in a background job, keeping the JSONB field as a read cache.