Context
backend/src/services/export/exports/google/contacts-api.ts:400 — mapToPerson has cyclomatic complexity 33 (DeepSource JS-R1005, "very-high" risk). The function is 130+ lines and handles 7+ different schema field types in one method.
The issue was flagged on PR #2830 (phone-only contacts) and intentionally not fixed in that PR because:
- It is pre-existing on
main (function is 129 lines on main; we added only 5 lines for the hasEmail guard in commit fcbcccd).
- The refactor is non-trivial (~30-60 min) and carries regression risk without dedicated test coverage.
- It is out of scope for the phone-only contacts feature.
Proposed approach
Split mapToPerson into smaller helper functions, one per schema field:
mapNames(contact, existing, updateEmptyOnly): Schema$Name[]
mapEmails(contact, existing, updateEmptyOnly): Schema$EmailAddress[]
mapPhones(contact, existing): Schema$PhoneNumber[]
mapOrgs(contact, existing): Schema$Organization[]
mapUrls(contact, existing): Schema$Url[]
mapAddresses(contact, existing): Schema$Address[]
mapMemberships(contact, existing, labels): Schema$Membership[]
Then mapToPerson becomes a ~10-line composer.
Existing test coverage
backend/test/unit/contact-export/googleContacts.test.ts:677 contains a describe('mapToPerson', ...) block that covers the happy path. May need additional branch coverage for the updateEmptyOnly flag and the per-field "is duplicate" checks.
Acceptance criteria
Context
backend/src/services/export/exports/google/contacts-api.ts:400—mapToPersonhas cyclomatic complexity 33 (DeepSource JS-R1005, "very-high" risk). The function is 130+ lines and handles 7+ different schema field types in one method.The issue was flagged on PR #2830 (phone-only contacts) and intentionally not fixed in that PR because:
main(function is 129 lines on main; we added only 5 lines for thehasEmailguard in commitfcbcccd).Proposed approach
Split
mapToPersoninto smaller helper functions, one per schema field:mapNames(contact, existing, updateEmptyOnly): Schema$Name[]mapEmails(contact, existing, updateEmptyOnly): Schema$EmailAddress[]mapPhones(contact, existing): Schema$PhoneNumber[]mapOrgs(contact, existing): Schema$Organization[]mapUrls(contact, existing): Schema$Url[]mapAddresses(contact, existing): Schema$Address[]mapMemberships(contact, existing, labels): Schema$Membership[]Then
mapToPersonbecomes a ~10-line composer.Existing test coverage
backend/test/unit/contact-export/googleContacts.test.ts:677contains adescribe('mapToPerson', ...)block that covers the happy path. May need additional branch coverage for theupdateEmptyOnlyflag and the per-field "is duplicate" checks.Acceptance criteria
mapToPersoncyclomatic complexity ≤ 20googleContacts.test.tstests pass