Current section
Files
Jump to
Current section
Files
lib/phoenix_kit/modules/emails/brevo_client.ex
defmodule PhoenixKit.Modules.Emails.BrevoClient do
@moduledoc """
Thin wrapper around the Brevo transactional-email events API
(`GET /v3/smtp/statistics/events`).
Isolated from `BrevoPollingJob` so tests can stub the HTTP layer with
`Req.Test` instead of hitting the real network: pass
`req_options: [plug: {Req.Test, MyStubName}]`.
"""
require Logger
@events_url "https://api.brevo.com/v3/smtp/statistics/events"
@http_timeout 15_000
@doc """
Fetches one page of events for the given date window.
`params` is merged into the query string as-is — callers pass Brevo's
real query keys (`startDate`, `endDate`, `limit`, `offset`, `sort`, ...).
## Returns
- `{:ok, [event_map]}` — the `events` array from a 200 response (`[]`
when the response has no `events` key, e.g. an empty page)
- `{:error, :invalid_credentials}` — 401
- `{:error, {:http_status, status}}` — any other non-200 status
- `{:error, reason}` — transport-level failure
"""
@spec fetch_events(String.t(), keyword(), keyword()) ::
{:ok, [map()]} | {:error, term()}
def fetch_events(api_key, params, req_options \\ []) do
request_opts =
[
headers: [{"api-key", api_key}],
params: params,
receive_timeout: @http_timeout
]
|> Keyword.merge(req_options)
case Req.get(@events_url, request_opts) do
{:ok, %{status: 200, body: %{"events" => events}}} when is_list(events) ->
{:ok, events}
{:ok, %{status: 200}} ->
{:ok, []}
{:ok, %{status: 401}} ->
{:error, :invalid_credentials}
{:ok, %{status: status}} ->
{:error, {:http_status, status}}
{:error, reason} ->
Logger.error("BrevoClient: request failed", %{reason: inspect(reason)})
{:error, reason}
end
end
end