critical priority medium complexity backend pending backend specialist Tier 1

Acceptance Criteria

FieldMappingResolver.load(orgId, integrationType) returns a FieldMapping object containing the parsed JSONB mapping for the given org and integration type
First call for a given (orgId, integrationType) pair fetches from Supabase; subsequent calls within TTL return cached result without network call
Default TTL is 5 minutes; TTL is configurable via constructor parameter for testability
Cache is keyed by (orgId + ':' + integrationType) string
FieldMappingResolver.invalidate(orgId, integrationType) removes the cached entry and forces next load() to fetch fresh data
If no mapping exists in database for the given org/type pair, load() returns an empty FieldMapping (not null, not exception)
FieldMapping contains a map of internalFieldName → externalFieldName entries
Loading a malformed JSONB value from the database logs a warning and returns empty FieldMapping rather than throwing
Resolver is thread-safe (Dart single-threaded but safe for concurrent async calls — no race on cache write)
Integration test: load Xledger mapping for org 'blindeforbundet-test', verify specific field aliases match seeded test data

Technical Requirements

frameworks
Flutter
Dart
Riverpod
Supabase Dart SDK
apis
Supabase PostgREST — integration_field_mappings table select by org_id and integration_type
data models
FieldMapping
FieldMappingEntry
integration_field_mappings (DB table)
performance requirements
Cache hit must return in under 1ms
Cache miss (database fetch) must complete within 2 seconds on a standard connection
Cache must not grow unbounded — implement max 100 entries with LRU eviction or TTL expiry sweep
security requirements
Only authenticated Supabase clients with org-scoped RLS policies may read field mappings
Resolver must not expose mappings across org boundaries — cache key must include orgId

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Integration Task

Handles integration between different epics or system components. Requires coordination across multiple development streams.

Implementation Notes

Implement the cache as a Map where _CacheEntry holds the FieldMapping and a DateTime expiry. Use a simple async lock pattern (Completer-based) to prevent duplicate in-flight fetches for the same key when multiple callers request the same uncached mapping simultaneously. The Supabase query should be: `supabase.from('integration_field_mappings').select('mapping_json').eq('org_id', orgId).eq('integration_type', integrationType).maybeSingle()`. Parse the returned JSONB as Map and convert to FieldMapping.

Keep the parsing logic in a separate static method for easy unit testing. Expose via Riverpod as an AsyncNotifierProvider or a simple Provider wrapping the class instance. Document clearly that this resolver only loads mappings — applying them to payloads is handled in task-004.

Testing Requirements

Unit tests using flutter_test with a mocked Supabase client. Tests: (1) first load triggers one database fetch, (2) second load within TTL returns cached result with zero database calls, (3) load after TTL expiry triggers a new database fetch, (4) invalidate() causes next load() to re-fetch, (5) missing mapping returns empty FieldMapping, (6) malformed JSONB returns empty FieldMapping and logs warning, (7) concurrent load() calls for same key result in exactly one database fetch (no thundering herd). Use a FakeAsync or manual clock to control TTL expiry in tests without real delays.

Component
Field Mapping Resolver
service medium
Epic Risks (3)
medium impact high prob technical

Each of the five external systems (Xledger, Dynamics, Cornerstone, Consio, Bufdir) has a different authentication flow, field schema, and error format. Forcing them into a uniform adapter interface may require compromises that result in leaky abstractions or make the adapter contract too complex to maintain.

Mitigation & Contingency

Mitigation: Design the IntegrationAdapter interface with a loose invoke() payload rather than a typed one, allowing each adapter to declare its own input/output schema. Use integration type metadata in the registry to document per-adapter quirks. Build Xledger first as the most documented API, then adapt the interface based on learnings.

Contingency: If the uniform interface cannot accommodate all five systems, split into two interface tiers: a simple polling/export adapter and a richer bidirectional adapter, with the registry declaring which tier each system implements.

medium impact high prob dependency

Development and testing of the Cornerstone and Consio adapters depends on NHF providing sandbox API access. If credentials or documentation are delayed, these adapters cannot be validated, blocking the epic's acceptance criteria.

Mitigation & Contingency

Mitigation: Implement Xledger and Dynamics adapters first (better-documented, sandbox available). Create a mock adapter for Cornerstone/Consio using recorded API responses for CI testing. Proactively request sandbox access from NHF at project kickoff.

Contingency: Ship the epic with Cornerstone/Consio adapters in a 'stub' state (connectivity test returns a simulated success, invoke() is not production-wired) and gate the NHF integration behind a feature flag until real API access is obtained.

medium impact medium prob scope

Real-world field mappings may include nested transformations, conditional logic, and data type coercions (e.g., Norwegian date formats, currency rounding rules) that the Field Mapping Resolver's initial design does not accommodate, requiring scope expansion mid-epic.

Mitigation & Contingency

Mitigation: Gather actual field mapping examples from Blindeforbundet (Xledger) and HLF (Dynamics) before designing the resolver. Identify the most complex transformation required and ensure the resolver design handles it. Limit Phase 1 to direct field renaming and format conversion only.

Contingency: If complex transformations are required, implement a simple expression evaluator (e.g., JSONata or a custom mini-DSL) as an extension point in the resolver, delivering basic mappings first and complex ones in a follow-up task.