View Source Glific.Contacts (Glific v5.1.6)

The Contacts context.

Link to this section Summary

Types

Options that may be passed to a request function. See request/2 for detailed descriptions.

Functions

Add permission specific to groups, in this case we want to restrict the visibility of groups that the user can see

Check if we can send a message to the contact

Check if we can send a session message to the contact

Check if we can send a session message to the contact with some extra parameters Specifically designed for when we are trying to optin an opted out contact

create new contact history record.

Returns an %Ecto.Changeset{} for tracking contact changes.

Get contact's current location

Update DB fields when contact opted in and ignore if it's blocked

count contact history

Return the count of contacts, using the same filter as list_contacts

Creates a contact.

Creates a location.

Perform a DELETE request.

Perform a DELETE request.

Deletes a contact.

Perform a GET request.

Perform a GET request.

Gets a single contact.

Gets a single contact by phone number.

Convert contact field to map for variable substitution

Perform a HEAD request.

Perform a HEAD request.

check if contact is blocked or not

Check if this contact id is a new contact. We set the last message number in the contact. So if this is a first message then we can assume that this is a new contact.

Checks if the contact passed in argument is organization root contact or not

Lets centralize the code to detect simulator messages and interaction

Get contact history

Returns the list of contacts.

Return the list of contacts who are also users

This function is called by the messaging framework for all incoming messages, hence might be a good candidate to maintain a contact level cache at some point

This function will be use just by ngo user where they can only update the contacts.

Opt out a contact if the provider returns an error code about Number not existing or not on whatsapp

Upload a contact phone as opted in

Perform a OPTIONS request.

Perform a OPTIONS request.

Perform a PATCH request.

Perform a PATCH request.

Perform a POST request.

Perform a POST request.

Perform a PUT request.

Perform a PUT request.

Perform request and raise in case of error.

Set session status for opted in and opted out contacts

Perform a TRACE request.

Perform a TRACE request.

Updates a contact.

Invoked from cron jobs to mass update the status of contacts belonging to a specific organization

Gets or Creates a Contact based on the unique indexes in the table. If there is a match it returns the existing contact, else it creates a new one

Link to this section Types

@type option() ::
  {:method, Tesla.Env.method()}
  | {:url, Tesla.Env.url()}
  | {:query, Tesla.Env.query()}
  | {:headers, Tesla.Env.headers()}
  | {:body, Tesla.Env.body()}
  | {:opts, Tesla.Env.opts()}

Options that may be passed to a request function. See request/2 for detailed descriptions.

Link to this section Functions

Link to this function

add_permission(query, user)

View Source
@spec add_permission(Ecto.Query.t(), Glific.Users.User.t()) :: Ecto.Query.t()

Add permission specific to groups, in this case we want to restrict the visibility of groups that the user can see

Link to this function

can_send_message_to?(contact)

View Source
@spec can_send_message_to?(Glific.Contacts.Contact.t()) ::
  {:ok | :error, String.t() | nil}

Check if we can send a message to the contact

Link to this function

can_send_message_to?(contact, is_hsm)

View Source
@spec can_send_message_to?(Glific.Contacts.Contact.t(), boolean()) ::
  {:ok | :error, String.t() | nil}

Check if we can send a session message to the contact

Link to this function

can_send_message_to?(contact, is_hsm, attrs)

View Source
@spec can_send_message_to?(Glific.Contacts.Contact.t(), boolean(), map()) ::
  {:ok | :error, String.t() | nil}

Check if we can send a session message to the contact with some extra parameters Specifically designed for when we are trying to optin an opted out contact

Link to this function

capture_history(contact_id, event_type, attrs)

View Source
@spec capture_history(Glific.Contacts.Contact.t() | non_neg_integer(), atom(), map()) ::
  {:ok, Glific.Contacts.ContactHistory.t()} | {:error, Ecto.Changeset.t()}

create new contact history record.

Link to this function

change_contact(contact, attrs \\ %{})

View Source
@spec change_contact(Glific.Contacts.Contact.t(), map()) :: Ecto.Changeset.t()

Returns an %Ecto.Changeset{} for tracking contact changes.

examples

Examples

iex> change_contact(contact)
%Ecto.Changeset{data: %Contact{}}
Link to this function

contact_location(contact)

View Source
@spec contact_location(Glific.Contacts.Contact.t()) ::
  {:ok, Glific.Contacts.Location.t()}

Get contact's current location

Link to this function

contact_opted_in(contact_attrs, organization_id, utc_time, opts \\ [])

View Source
@spec contact_opted_in(map(), non_neg_integer(), DateTime.t(), Keyword.t()) ::
  {:ok, Glific.Contacts.Contact.t()} | {:error, Ecto.Changeset.t()}

Update DB fields when contact opted in and ignore if it's blocked

Link to this function

contact_opted_out(phone, organization_id, utc_time, method \\ "Glific Flows")

View Source
@spec contact_opted_out(String.t(), non_neg_integer(), DateTime.t(), String.t()) ::
  :ok | :error

Update DB fields when contact opted out

Link to this function

count_contact_history(args)

View Source
@spec count_contact_history(map()) :: integer()

count contact history

@spec count_contacts(map()) :: integer()

Return the count of contacts, using the same filter as list_contacts

@spec create_contact(map()) ::
  {:ok, Glific.Contacts.Contact.t()} | {:error, Ecto.Changeset.t()}

Creates a contact.

examples

Examples

iex> create_contact(%{field: value})
{:ok, %Contact{}}

iex> create_contact(%{field: bad_value})
{:error, %Ecto.Changeset{}}
Link to this function

create_location(attrs \\ %{})

View Source
@spec create_location(map()) ::
  {:ok, Glific.Contacts.Location.t()} | {:error, Ecto.Changeset.t()}

Creates a location.

examples

Examples

iex> Glific.Contacts.create_location(%{name: value})
{:ok, %Glific.Contacts.Location{}}

iex> Glific.Contacts.create_location(%{bad_field: bad_value})
{:error, %Ecto.Changeset{}}
Link to this function

delete(client, url, opts)

View Source
@spec delete(Tesla.Env.client(), Tesla.Env.url(), [option()]) :: Tesla.Env.result()

Perform a DELETE request.

See request/1 or request/2 for options definition.

delete("/users")
delete("/users", query: [scope: "admin"])
delete(client, "/users")
delete(client, "/users", query: [scope: "admin"])
delete(client, "/users", body: %{name: "Jon"})
Link to this function

delete!(client, url, opts)

View Source
@spec delete!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()

Perform a DELETE request.

See request!/1 or request!/2 for options definition.

delete!("/users")
delete!("/users", query: [scope: "admin"])
delete!(client, "/users")
delete!(client, "/users", query: [scope: "admin"])
delete!(client, "/users", body: %{name: "Jon"})
@spec delete_contact(Glific.Contacts.Contact.t()) ::
  {:ok, Glific.Contacts.Contact.t()} | {:error, Ecto.Changeset.t()}

Deletes a contact.

examples

Examples

iex> delete_contact(contact)
{:ok, %Contact{}}

iex> delete_contact(contact)
{:error, %Ecto.Changeset{}}

Perform a GET request.

See request/1 or request/2 for options definition.

get("/users")
get("/users", query: [scope: "admin"])
get(client, "/users")
get(client, "/users", query: [scope: "admin"])
get(client, "/users", body: %{name: "Jon"})
@spec get!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()

Perform a GET request.

See request!/1 or request!/2 for options definition.

get!("/users")
get!("/users", query: [scope: "admin"])
get!(client, "/users")
get!(client, "/users", query: [scope: "admin"])
get!(client, "/users", body: %{name: "Jon"})
@spec get_contact!(integer()) :: Glific.Contacts.Contact.t()

Gets a single contact.

Raises Ecto.NoResultsError if the Contact does not exist.

examples

Examples

iex> get_contact!(123)
%Contact{}

iex> get_contact!(456)
** (Ecto.NoResultsError)
Link to this function

get_contact_by_phone!(phone)

View Source
@spec get_contact_by_phone!(String.t()) :: Glific.Contacts.Contact.t()

Gets a single contact by phone number.

Raises Ecto.NoResultsError if the Contact does not exist.

examples

Examples

iex> get_contact_by_phone!("9876543210_1")
%Contact{}

iex> get_contact!("123")
** (Ecto.NoResultsError)
Link to this function

get_contact_field_map(contact_id)

View Source
@spec get_contact_field_map(integer()) :: map()

Convert contact field to map for variable substitution

Perform a HEAD request.

See request/1 or request/2 for options definition.

head("/users")
head("/users", query: [scope: "admin"])
head(client, "/users")
head(client, "/users", query: [scope: "admin"])
head(client, "/users", body: %{name: "Jon"})
Link to this function

head!(client, url, opts)

View Source
@spec head!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()

Perform a HEAD request.

See request!/1 or request!/2 for options definition.

head!("/users")
head!("/users", query: [scope: "admin"])
head!(client, "/users")
head!(client, "/users", query: [scope: "admin"])
head!(client, "/users", body: %{name: "Jon"})
Link to this function

is_contact_blocked?(contact)

View Source
@spec is_contact_blocked?(Glific.Contacts.Contact.t()) :: boolean()

check if contact is blocked or not

Link to this function

is_new_contact(contact_id)

View Source
@spec is_new_contact(integer()) :: boolean()

Check if this contact id is a new contact. We set the last message number in the contact. So if this is a first message then we can assume that this is a new contact.

Link to this function

is_org_root_contact?(contact)

View Source
@spec is_org_root_contact?(Glific.Contacts.Contact.t()) :: boolean()

Checks if the contact passed in argument is organization root contact or not

Link to this function

is_simulator_contact?(phone)

View Source
@spec is_simulator_contact?(String.t()) :: boolean()

Lets centralize the code to detect simulator messages and interaction

Link to this function

list_contact_history(args)

View Source
@spec list_contact_history(map()) :: [Glific.Contacts.ContactHistory.t()]

Get contact history

@spec list_contacts(map()) :: [Glific.Contacts.Contact.t()]

Returns the list of contacts.

examples

Examples

iex> list_contacts()
[%Contact{}, ...]

Get the list of contacts filtered by various search options Include contacts only if within list of groups Include contacts only if have list of tags

Link to this function

list_user_contacts(args \\ %{})

View Source
@spec list_user_contacts(map()) :: [Glific.Contacts.Contact.t()]

Return the list of contacts who are also users

Link to this function

maybe_create_contact(sender)

View Source
@spec maybe_create_contact(map()) ::
  {:ok, Glific.Contacts.Contact.t()} | {:error, Ecto.Changeset.t()}

This function is called by the messaging framework for all incoming messages, hence might be a good candidate to maintain a contact level cache at some point

We use a fetch followed by create, to avoid the explosion in the id namespace. We also avoid updating the contact to skip the DB call, and only do so if the name has changed

Link to this function

maybe_update_contact(sender)

View Source
@spec maybe_update_contact(map()) ::
  {:ok, Glific.Contacts.Contact.t()}
  | {:error, Ecto.Changeset.t()}
  | {:error, any()}

This function will be use just by ngo user where they can only update the contacts.

Link to this function

number_does_not_exist(contact_id, method \\ "Number does not exist")

View Source
@spec number_does_not_exist(non_neg_integer(), String.t()) :: any()

Opt out a contact if the provider returns an error code about Number not existing or not on whatsapp

@spec optin_contact(map()) ::
  {:ok, Glific.Contacts.Contact.t()}
  | {:error, Ecto.Changeset.t()}
  | {:error, String.t()}

Upload a contact phone as opted in

Link to this function

options(client, url, opts)

View Source
@spec options(Tesla.Env.client(), Tesla.Env.url(), [option()]) :: Tesla.Env.result()

Perform a OPTIONS request.

See request/1 or request/2 for options definition.

options("/users")
options("/users", query: [scope: "admin"])
options(client, "/users")
options(client, "/users", query: [scope: "admin"])
options(client, "/users", body: %{name: "Jon"})
Link to this function

options!(client, url, opts)

View Source
@spec options!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()

Perform a OPTIONS request.

See request!/1 or request!/2 for options definition.

options!("/users")
options!("/users", query: [scope: "admin"])
options!(client, "/users")
options!(client, "/users", query: [scope: "admin"])
options!(client, "/users", body: %{name: "Jon"})
Link to this function

patch(client, url, body, opts)

View Source

Perform a PATCH request.

See request/1 or request/2 for options definition.

patch("/users", %{name: "Jon"})
patch("/users", %{name: "Jon"}, query: [scope: "admin"])
patch(client, "/users", %{name: "Jon"})
patch(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Link to this function

patch!(client, url, body, opts)

View Source

Perform a PATCH request.

See request!/1 or request!/2 for options definition.

patch!("/users", %{name: "Jon"})
patch!("/users", %{name: "Jon"}, query: [scope: "admin"])
patch!(client, "/users", %{name: "Jon"})
patch!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Link to this function

post(client, url, body, opts)

View Source

Perform a POST request.

See request/1 or request/2 for options definition.

post("/users", %{name: "Jon"})
post("/users", %{name: "Jon"}, query: [scope: "admin"])
post(client, "/users", %{name: "Jon"})
post(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Link to this function

post!(client, url, body, opts)

View Source

Perform a POST request.

See request!/1 or request!/2 for options definition.

post!("/users", %{name: "Jon"})
post!("/users", %{name: "Jon"}, query: [scope: "admin"])
post!(client, "/users", %{name: "Jon"})
post!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Link to this function

put(client, url, body, opts)

View Source

Perform a PUT request.

See request/1 or request/2 for options definition.

put("/users", %{name: "Jon"})
put("/users", %{name: "Jon"}, query: [scope: "admin"])
put(client, "/users", %{name: "Jon"})
put(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Link to this function

put!(client, url, body, opts)

View Source

Perform a PUT request.

See request!/1 or request!/2 for options definition.

put!("/users", %{name: "Jon"})
put!("/users", %{name: "Jon"}, query: [scope: "admin"])
put!(client, "/users", %{name: "Jon"})
put!(client, "/users", %{name: "Jon"}, query: [scope: "admin"])
Link to this function

request(client \\ %Tesla.Client{}, options)

View Source
@spec request(Tesla.Env.client(), [option()]) :: Tesla.Env.result()

Perform a request.

options

Options

  • :method - the request method, one of [:head, :get, :delete, :trace, :options, :post, :put, :patch]
  • :url - either full url e.g. "http://example.com/some/path" or just "/some/path" if using Tesla.Middleware.BaseUrl
  • :query - a keyword list of query params, e.g. [page: 1, per_page: 100]
  • :headers - a keyworld list of headers, e.g. [{"content-type", "text/plain"}]
  • :body - depends on used middleware:
    • by default it can be a binary
    • if using e.g. JSON encoding middleware it can be a nested map
    • if adapter supports it it can be a Stream with any of the above
  • :opts - custom, per-request middleware or adapter options

examples

Examples

ExampleApi.request(method: :get, url: "/users/path")

# use shortcut methods
ExampleApi.get("/users/1")
ExampleApi.post(client, "/users", %{name: "Jon"})
Link to this function

request!(client \\ %Tesla.Client{}, options)

View Source
@spec request!(Tesla.Env.client(), [option()]) :: Tesla.Env.t() | no_return()

Perform request and raise in case of error.

This is similar to request/2 behaviour from Tesla 0.x

See request/2 for list of available options.

Link to this function

set_session_status(contact, status)

View Source
@spec set_session_status(Glific.Contacts.Contact.t() | [non_neg_integer()], atom()) ::
  {:ok, Glific.Contacts.Contact.t()} | {:error, Ecto.Changeset.t()} | :ok

Set session status for opted in and opted out contacts

Link to this function

trace(client, url, opts)

View Source

Perform a TRACE request.

See request/1 or request/2 for options definition.

trace("/users")
trace("/users", query: [scope: "admin"])
trace(client, "/users")
trace(client, "/users", query: [scope: "admin"])
trace(client, "/users", body: %{name: "Jon"})
Link to this function

trace!(client, url, opts)

View Source
@spec trace!(Tesla.Env.client(), Tesla.Env.url(), [option()]) ::
  Tesla.Env.t() | no_return()

Perform a TRACE request.

See request!/1 or request!/2 for options definition.

trace!("/users")
trace!("/users", query: [scope: "admin"])
trace!(client, "/users")
trace!(client, "/users", query: [scope: "admin"])
trace!(client, "/users", body: %{name: "Jon"})
Link to this function

update_contact(contact, attrs)

View Source
@spec update_contact(Glific.Contacts.Contact.t(), map()) ::
  {:ok, Glific.Contacts.Contact.t()} | {:error, Ecto.Changeset.t()}

Updates a contact.

examples

Examples

iex> update_contact(contact, %{field: new_value})
{:ok, %Contact{}}

iex> update_contact(contact, %{field: bad_value})
{:error, %Ecto.Changeset{}}
Link to this function

update_contact_status(organization_id, args)

View Source
@spec update_contact_status(non_neg_integer(), map()) :: :ok

Invoked from cron jobs to mass update the status of contacts belonging to a specific organization

In this case, if we can, we might want to do it across the entire DB since the update is across all organizations. The main issue might be the row level security of postgres and how it ties in. For now, lets stick to per organization

@spec upsert(map()) :: {:ok, Glific.Contacts.Contact.t()}

Gets or Creates a Contact based on the unique indexes in the table. If there is a match it returns the existing contact, else it creates a new one