Skip vCards on re-import that the address book already has #228

Closed
opened 2026-09-02 16:34:08 +00:00 by jcoffey-dev · 0 comments
Owner

Refs #223 — the vCard half. The LDIF half stays open, deliberately.

The contacts version of the rule that shipped for events in #225, and the only half that can be decided without an answer from the reporter. A vCard carries a UID its author meant, so a card whose UID a book already holds is that card, and re-importing an export left a second copy of every one of them.

Reported on #174 by the reporter's colleague; the rule decided on #173:

Both of us would prefer duplicate checks on UIDs if UID present.

The rule

  • A card whose UID this book already holds is skipped.
  • A card that arrives without a UID is imported, as before — nothing can be matched on an identity that is not there.
  • Matching is per book. The same person filed in two address books is not a duplicate, any more than the same event in two calendars is.

LDIF is untouched, and now says so in the type

Mozilla's schema defines no UID, and the dn is not an identity outside the directory it came from — which is why the import already invents a UID, one that can never match anything present. Guessing instead from a name plus an address is the open question on #223, and a guess that merges two people who share a name is worse than a duplicate somebody can see and delete.

importLdif answers in the same shape as importVCard so a caller need not know which it called; its skipped is always 0, which is the honest number rather than a missing field.

Two callers, two messages

  • The contacts import reports both counts, as the calendar's does. "Imported 3 contacts" over a file of two hundred reads as a failure when the rest were simply already there.
  • A vCard attached to a message — usually one you have been sent before — now says it is already in your contacts rather than reporting that it added none.

Where the UIDs come from

Asked of the server, not read from the cards in the store. The store's copy is complete once the contacts view has loaded, and importing does not wait for a view; a scan that is right whatever the client happens to be holding costs one pass over a list nobody imports into twice a day. Same shape as the calendar's scan.

Tests

Six new: skipping a UID already in the book, importing one whose UID is in a different book, still importing a card with no UID, sending no set at all when the whole file is present, the empty-book case, and one pinning that LDIF imports the same file twice over and reports nothing skipped — so the gap is a decision on the record rather than something that looks like a bug later.

Existing LDIF and batching tests move to the { created, skipped } shape.

npm run typecheck, npm test (946 web + 122 server), npm run build, i18n:check (no new stale keys) pass. Two new plurals across all nine catalogues — mine, not native-reviewed.

Merged 2026-09-02 as coffey-labs/ihasmail@5f35428ddb

Rebuilt from: git history, session transcript.

Refs #223 — the vCard half. The LDIF half stays open, deliberately. The contacts version of the rule that shipped for events in #225, and the only half that can be decided without an answer from the reporter. A vCard carries a UID its author meant, so a card whose UID a book already holds is that card, and re-importing an export left a second copy of every one of them. Reported on [#174](https://github.com/Coffey-Labs/ihasmail/issues/174#issuecomment-5507056538) by the reporter's colleague; the rule decided on [#173](https://github.com/Coffey-Labs/ihasmail/issues/173#issuecomment-5510955196): > Both of us would prefer duplicate checks on UIDs if UID present. ## The rule - A card whose UID this book already holds is **skipped**. - A card that arrives **without** a UID is imported, as before — nothing can be matched on an identity that is not there. - Matching is **per book**. The same person filed in two address books is not a duplicate, any more than the same event in two calendars is. ## LDIF is untouched, and now says so in the type Mozilla's schema defines no UID, and the `dn` is not an identity outside the directory it came from — which is why the import already invents a UID, one that can never match anything present. Guessing instead from a name plus an address is the open question on #223, and a guess that merges two people who share a name is worse than a duplicate somebody can see and delete. `importLdif` answers in the same shape as `importVCard` so a caller need not know which it called; its `skipped` is always `0`, which is the honest number rather than a missing field. ## Two callers, two messages - **The contacts import** reports both counts, as the calendar's does. "Imported 3 contacts" over a file of two hundred reads as a failure when the rest were simply already there. - **A vCard attached to a message** — usually one you have been sent before — now says it is already in your contacts rather than reporting that it added none. ## Where the UIDs come from Asked of the server, not read from the cards in the store. The store's copy is complete once the contacts view has loaded, and importing does not wait for a view; a scan that is right whatever the client happens to be holding costs one pass over a list nobody imports into twice a day. Same shape as the calendar's scan. ## Tests Six new: skipping a UID already in the book, importing one whose UID is in a *different* book, still importing a card with no UID, sending no `set` at all when the whole file is present, the empty-book case, and one pinning that LDIF imports the same file twice over and reports nothing skipped — so the gap is a decision on the record rather than something that looks like a bug later. Existing LDIF and batching tests move to the `{ created, skipped }` shape. `npm run typecheck`, `npm test` (946 web + 122 server), `npm run build`, `i18n:check` (no new stale keys) pass. Two new plurals across all nine catalogues — mine, not native-reviewed. **Merged** 2026-09-02 as coffey-labs/ihasmail@5f35428ddb21 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.