defmodule SendGrid do
@moduledoc """
Interface to SendGrid's API.
## Configuration
An API key can be set in your application's config.
# Compile-time configured key.
config :noizu_sendgrid,
api_key: "sendgrid_api_key"
# Run-time configured key
config :noizu_sendgrid,
api_key: {:system, "ENV_KEY"}
Optionally you can supply an API key as a keyword option in the last argument
of any API call to override and set the API key to use for the request.
SendGrid.Mail.send(..., api_key: "API_KEY")
## Usage
Most usage with this library will be with composing transactional emails.
Refer to `SendGrid.Email` for full documentation and usage.
"""
alias SendGrid.Response
@type api_key :: {:api_key, String.t()}
@type query :: {:query, Keyword.t()}
@type page :: {:page, pos_integer()}
@type page_size :: {:page_size, pos_integer()}
@typedoc """
Optional arguments to use when performing a request.
"""
@type options :: [query | api_key]
@doc """
Performs a GET request.
## Options
* `:api_key` - API key to use with the request.
* `:query` - Keyword list of query params to use with the request.
"""
@spec get(path :: String.t(), options :: options()) :: {:ok, Response.t()} | {:error, any()}
def get(path, opts \\ []) when is_list(opts) do
opts
|> api_key()
|> build_client()
|> Tesla.get(path, query_opts(opts))
|> parse_response()
end
@doc """
Performs a POST request.
## Options
* `:api_key` - API key to use with the request.
* `:query` - Keyword list of query params to use with the request.
"""
@spec post(path :: String.t(), body :: map(), options :: options()) ::
{:ok, Response.t()} | {:error, any()}
def post(path, body, opts \\ []) when is_map(body) and is_list(opts) do
opts
|> api_key()
|> build_client()
|> Tesla.post(path, body, query_opts(opts))
|> parse_response()
end
@doc """
Performs a PATCH request.
## Options
* `:api_key` - API key to use with the request.
* `:query` - Keyword list of query params to use with the request.
"""
@spec patch(path :: String.t(), body :: map(), options :: options()) ::
{:ok, Response.t()} | {:error, any()}
def patch(path, body, opts \\ []) when is_map(body) and is_list(opts) do
opts
|> api_key()
|> build_client()
|> Tesla.patch(path, body, query_opts(opts))
|> parse_response()
end
@doc """
Performs a DELETE request.
## Options
* `:api_key` - API key to use with the request.
* `:query` - Keyword list of query params to use with the request.
"""
@spec delete(path :: String.t(), options :: options()) :: {:ok, Response.t()} | {:error, any()}
def delete(path, opts \\ []) when is_list(opts) do
opts
|> api_key()
|> build_client()
|> Tesla.delete(path, query_opts(opts))
|> parse_response()
end
defp api_key(opts) do
api_key = Keyword.get(opts, :api_key) || runtime_key()
unless api_key do
raise RuntimeError, """
No API key is configured for SendGrid. Update your config your pass in a
key with `:api_key` as an addional request option.
SendGrid.get("/stats", api_key: "API_KEY")
config :noizu_sendgrid,
api_key: "sendgrid_api_key"
config :noizu_sendgrid,
api_key: {:system, "SENDGRID_KEY"}
"""
end
api_key
end
defp runtime_key do
case Application.get_env(:noizu_sendgrid, :api_key) do
{:system, env_key} -> System.get_env(env_key)
key -> key
end
end
defp build_client(api_key) do
middleware = [
{Tesla.Middleware.BaseUrl, "https://api.sendgrid.com"},
Tesla.Middleware.JSON,
{Tesla.Middleware.Headers, [{"Authorization", "Bearer #{api_key}"}]}
]
Tesla.client(middleware)
end
defp query_opts(opts) do
Keyword.take(opts, [:query])
end
defp parse_response({:ok, %{body: body, headers: headers, status: status}}) do
{:ok, %Response{body: body, headers: headers, status: status}}
end
defp parse_response({:error, _} = error), do: error
end