defmodule Glific.Flows.ContactField do
@moduledoc """
Since many of the functions set/update fields in contact and related tables, lets
centralize all the code here for now
"""
alias Glific.{
Contacts,
Contacts.Contact,
Contacts.ContactsField,
Flows.FlowContext,
Flows.MessageVarParser,
Profiles,
Repo
}
@doc """
Add a field {key, value} to a contact. For now, all preferences are stored under the
settings map, with a sub-map of preferences. We expect to get more clarity on this soon
"""
@spec add_contact_field(FlowContext.t(), String.t(), String.t(), String.t(), String.t()) ::
FlowContext.t()
def add_contact_field(context, field, label, value, type) do
contact = do_add_contact_field(context.contact, field, label, value, type)
Map.put(context, :contact, contact)
end
@doc """
Add contact field taking contact as parameter. We should change the name of this function for the consistency
"""
@spec do_add_contact_field(Contact.t(), String.t(), String.t(), any(), String.t()) ::
Contact.t()
def do_add_contact_field(contact, field, label, value, type \\ "string") do
contact_fields =
if is_nil(contact.fields),
do: %{},
else: contact.fields
fields =
contact_fields
|> Map.put(field, %{value: value, label: label, type: type, inserted_at: DateTime.utc_now()})
{:ok, contact} =
Contacts.update_contact(
contact,
%{fields: fields}
)
# update profile fields if active profile is set for a contact
maybe_update_profile_field(contact, fields)
# create contact fields if not already created
maybe_create_contact_field(%{
shortcode: field,
name: label,
organization_id: contact.organization_id
})
{:ok, _} =
Contacts.capture_history(contact, :contact_fields_updated, %{
event_meta: %{
field: %{
data: field,
label: label,
value: value,
old_value: get_in(contact_fields, [field]),
new_value: value
}
},
event_label: "Value for #{label} is updated to #{value}"
})
contact
end
@doc """
Reset the fields for a contact.
"""
@spec reset_contact_fields(FlowContext.t()) :: FlowContext.t()
def reset_contact_fields(context) do
{:ok, contact} =
Contacts.update_contact(
context.contact,
%{fields: %{}}
)
{:ok, _} =
Contacts.capture_history(contact, :contact_fields_reset, %{
event_label: "All contact fields are reset"
})
Map.put(context, :contact, contact)
end
@doc """
parse contact fields values with check if it has
"""
@spec parse_contact_field_value(FlowContext.t(), String.t()) :: String.t()
def parse_contact_field_value(context, value) do
message_vars = FlowContext.get_vars_to_parse(context)
value
|> MessageVarParser.parse(message_vars)
|> Glific.execute_eex()
end
@doc """
list contacts fields.
"""
@spec list_contacts_fields(map()) :: [ContactsField.t()]
def list_contacts_fields(args) do
Repo.list_filter(args, ContactsField, &Repo.opts_with_inserted_at/2, &Repo.filter_with/2)
|> Enum.map(fn contacts_field ->
add_variable_field(contacts_field)
end)
end
@spec add_variable_field(ContactsField.t()) :: map()
defp add_variable_field(contacts_field) do
contacts_field
|> Map.put(:variable, "@contact.fields.#{contacts_field.shortcode}")
end
@doc """
Return the count of contacts_fields, using the same filter as list_contacts_fields
"""
@spec count_contacts_fields(map()) :: integer
def count_contacts_fields(args),
do: Repo.count_filter(args, ContactsField, &Repo.filter_with/2)
@doc """
Create contact field
"""
@spec create_contact_field(map()) :: {:ok, ContactsField.t()} | {:error, Ecto.Changeset.t()}
def create_contact_field(attrs) do
with {:ok, contacts_field} <-
%ContactsField{}
|> ContactsField.changeset(attrs)
|> Repo.insert() do
contacts_field = add_variable_field(contacts_field)
{:ok, contacts_field}
end
end
@doc """
Create or update contact field
"""
@spec maybe_create_contact_field(map()) ::
{:ok, ContactsField.t()} | {:error, Ecto.Changeset.t()}
def maybe_create_contact_field(attrs) do
case Repo.get_by(ContactsField, %{shortcode: attrs.shortcode},
organization_id: attrs.organization_id
) do
nil ->
create_contact_field(attrs)
contact_field ->
update_contacts_field(contact_field, attrs)
end
end
@doc """
Update profile field if there is an active profile id set
"""
@spec maybe_update_profile_field(Contact.t(), map()) ::
Contact.t()
def maybe_update_profile_field(%{active_profile_id: active_profile_id} = contact, fields)
when is_integer(active_profile_id) do
with {:ok, profile} <- Repo.fetch_by(Profiles.Profile, %{id: active_profile_id}) do
Profiles.update_profile(profile, %{fields: fields})
end
contact
end
def maybe_update_profile_field(contact, _fields), do: contact
@doc """
Updates a contact field.
## Examples
iex> update_contacts_field(contacts_field, %{field: new_value})
{:ok, %ContactsField{}}
iex> update_contacts_field(contacts_field, %{field: bad_value})
{:error, %Ecto.Changeset{}}
"""
@spec update_contacts_field(ContactsField.t(), map()) ::
{:ok, ContactsField.t()} | {:error, Ecto.Changeset.t()}
def update_contacts_field(%ContactsField{} = contacts_field, attrs) do
contacts_field
|> ContactsField.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a contact field.
## Examples
iex> delete_contacts_field(contacts_field)
{:ok, %ContactsField{}}
iex> delete_contacts_field(contacts_field)
{:error, %Ecto.Changeset{}}
"""
@spec delete_contacts_field(ContactsField.t()) ::
{:ok, ContactsField.t()} | {:error, Ecto.Changeset.t()}
def delete_contacts_field(%ContactsField{} = contacts_field) do
contacts_field
|> ContactsField.changeset(%{})
|> Repo.delete()
end
end