pentacent / keila

Open Source Newsletter Tool.
https://keila.io
GNU Affero General Public License v3.0
1.36k stars 67 forks source link

Allow updating existing contacts on import #87

Closed wmnnd closed 2 years ago

wmnnd commented 2 years ago

It should be possible to update existing contacts on import, i.e. merging/replacing custom data or updating first/last name.

gbottari commented 2 years ago

Hi, I'm interested in working on this issue if that's ok.

If I understand correctly, you need to check the database for existing contacts (by e-mail, I presume) and replace their infos (like first/last name). That logic would go into Keila.Contacts.Import, correct? Any tips would be welcome!

wmnnd commented 2 years ago

Hey @gbottari, awesome, that would be very cool!

I think the import feature should by expanded with the following features:

  1. Modify Keila.contacts.Import.import_csv/2 to update existing entries with upserts (I think this would make sense as the default behavior)
  2. Optionally: Add an option to import_csv that would allow for upserts to be disabled + a checkbox in the UI to reflect that
  3. If you’re up for it, we should add a new field external_id (with a type adequate for storing arbitrary IDs, e.g. varchar(36)) with a unique index (project_id, external_id) that takes precedence over the email address when it comes to updating. This would allow people to keep Keila in sync with an external system and also allow for emails to be changed during imports.

Oh, and you should make sure to work off my branch with the updated UI if you want to make any UI changes. But I’ll probably merge it tomorrow :slightly_smiling_face:

Edit: It actually might even make sense to refactor the import a little bit so that we add something like Keila.Contacts.Import.import_contacts/2 which would take a list of Contact structs and opts as arguments. This function could then be reused in an import API or Webhook (see #75)

gbottari commented 2 years ago

Hey @wmnnd! Me and @panoramix360 are working on this issue now. We figured that we could use Postgres' own upsert feature through Ecto (https://hexdocs.pm/ecto/Ecto.Repo.html#c:insert/2-upserts). What we ended up with is the following:

Repo.insert(changeset, returning: false, on_conflict: {:replace_all_except, [:email, :project_id]}, conflict_target: [:email, :project_id])

We'll also look into the other tasks you mentioned.