defmodule ProviderService.Projections.ProviderProjection do use Commanded.Projections.Ecto, application: ProviderService.CommandedApp, repo: ProviderService.Repo, name: "ProviderProjection", consistency: :strong alias ProviderService.Events.{ ProviderRegistered, ProviderUpdated, ProviderDeactivated, ProviderReactivated, ProviderTemplateAdded, ProviderTemplateActivated, ProviderTemplateDeactivated, ProviderTemplateDefaultSet, ProviderTemplateRemoved } alias ProviderService.Projections.Provider import Ecto.Query project(%ProviderRegistered{} = e, _meta, fn multi -> Ecto.Multi.insert(multi, :provider, %Provider{ provider_id: e.provider_id, name: e.name, email: e.email, phone: e.phone, contact_name: e.contact_name, ruc: e.ruc, address: e.address, active: true, templates: %{}, default_templates: %{} }) end) project(%ProviderUpdated{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(Provider, e.provider_id)} end) |> Ecto.Multi.update(:provider, fn %{fetch: p} -> Ecto.Changeset.change(p, name: e.name, email: e.email, phone: e.phone, contact_name: e.contact_name, ruc: e.ruc, address: e.address ) end) end) project(%ProviderDeactivated{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(Provider, e.provider_id)} end) |> Ecto.Multi.update(:provider, fn %{fetch: p} -> Ecto.Changeset.change(p, active: false) end) end) project(%ProviderReactivated{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(Provider, e.provider_id)} end) |> Ecto.Multi.update(:provider, fn %{fetch: p} -> Ecto.Changeset.change(p, active: true) end) end) # templates: %{ policy_type => %{ client_type => [template] } } # default_templates: %{ policy_type => %{ client_type => template_id } } project(%ProviderTemplateAdded{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(Provider, e.provider_id)} end) |> Ecto.Multi.update(:provider, fn %{fetch: p} -> template = %{ "template_id" => e.template_id, "client_type" => e.client_type, "document_url" => e.document_url, "active" => false } updated = p.templates |> Map.update(e.policy_type, %{e.client_type => [template]}, fn inner -> Map.update(inner, e.client_type, [template], fn list -> list ++ [template] end) end) Ecto.Changeset.change(p, templates: updated) end) end) project(%ProviderTemplateActivated{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(Provider, e.provider_id)} end) |> Ecto.Multi.update(:provider, fn %{fetch: p} -> updated = update_template_field( p.templates, e.policy_type, e.client_type, e.template_id, "active", true ) Ecto.Changeset.change(p, templates: updated) end) end) project(%ProviderTemplateDeactivated{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(Provider, e.provider_id)} end) |> Ecto.Multi.update(:provider, fn %{fetch: p} -> updated = update_template_field( p.templates, e.policy_type, e.client_type, e.template_id, "active", false ) template_id = e.template_id default_templates = case get_in(p.default_templates, [e.policy_type, e.client_type]) do ^template_id -> Map.update(p.default_templates, e.policy_type, %{}, &Map.delete(&1, e.client_type)) _ -> p.default_templates end Ecto.Changeset.change(p, templates: updated, default_templates: default_templates) end) end) project(%ProviderTemplateDefaultSet{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(Provider, e.provider_id)} end) |> Ecto.Multi.update(:provider, fn %{fetch: p} -> default_templates = p.default_templates |> Map.update(e.policy_type, %{e.client_type => e.template_id}, fn inner -> Map.put(inner, e.client_type, e.template_id) end) Ecto.Changeset.change(p, default_templates: default_templates) end) end) project(%ProviderTemplateRemoved{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(Provider, e.provider_id)} end) |> Ecto.Multi.update(:provider, fn %{fetch: p} -> updated = p.templates |> Map.update(e.policy_type, %{}, fn inner -> Map.update(inner, e.client_type, [], fn list -> Enum.reject(list, &(&1["template_id"] == e.template_id)) end) end) template_id = e.template_id default_templates = case get_in(p.default_templates, [e.policy_type, e.client_type]) do ^template_id -> Map.update(p.default_templates, e.policy_type, %{}, &Map.delete(&1, e.client_type)) _ -> p.default_templates end Ecto.Changeset.change(p, templates: updated, default_templates: default_templates) end) end) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- defp update_template_field(templates, policy_type, client_type, template_id, field, value) do Map.update(templates, policy_type, %{}, fn inner -> Map.update(inner, client_type, [], fn list -> Enum.map(list, fn t -> if t["template_id"] == template_id, do: Map.put(t, field, value), else: t end) end) end) end end