Implement field mapping save and update in Config Service
epic-external-system-integration-configuration-core-services-task-012 — Extend the Integration Config Service to handle field mapping configuration saves and updates. Stores JSONB field mapping blobs per org per integration type, validates mapping completeness against the target system's required fields (via adapter capabilities), and triggers FieldMappingResolver cache invalidation on update to ensure fresh mappings are used immediately.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Handles integration between different epics or system components. Requires coordination across multiple development streams.
Implementation Notes
Use Supabase's upsert() with onConflict: 'org_id,integration_type' to handle concurrent saves safely. The FieldMapping domain object should be a Dart sealed class or freezed union to distinguish 'not yet saved', 'saved', and 'validation error' states cleanly for BLoC consumption. Store the JSONB blob as a Map
Keep the Config Service as a pure Dart class (no Flutter dependency) in the domain/services layer so it can be tested without Flutter test infrastructure. Cache invalidation should be injected as a dependency (FieldMappingResolverInvalidator interface) so it can be mocked in tests. Do not call getCapabilities() more than once per save operation — cache the result in a local variable.
Testing Requirements
Unit tests: mock the Supabase client and adapter registry to test save/update logic in isolation. Cover: (1) happy path save with all required fields mapped, (2) save missing required fields returns correct error structure, (3) malformed JSON blob is rejected, (4) cache invalidation is called exactly once on successful save, (5) cache invalidation is NOT called when save fails validation, (6) cross-org save attempt returns AuthorizationError, (7) idempotent upsert does not create duplicate records. Integration tests (against Supabase test instance): verify JSONB is stored and retrieved correctly, confirm RLS prevents cross-org reads, confirm audit log entry is created. Target 90%+ branch coverage on the save/update methods.
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.
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.
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.