tusker_push/lib/tusker_push/apns.ex

148 lines
4.4 KiB
Elixir
Raw Normal View History

2024-04-06 03:50:28 +00:00
defmodule TuskerPush.Apns do
2024-04-24 14:28:55 +00:00
require OpenTelemetry.Tracer
2024-04-06 19:02:18 +00:00
require Logger
2024-04-12 20:16:46 +00:00
@type environment() :: :development | :production
@spec send(environment(), String.t(), Map.t()) :: :ok | {:error, term()}
def send(apns_env, apns_device_token, payload) do
2024-04-25 03:08:05 +00:00
:telemetry.execute([:apns, :push], %{monotonic_time: System.monotonic_time()})
2024-04-24 14:28:55 +00:00
OpenTelemetry.Tracer.with_span :apns_push, %{attributes: [env: apns_env]} do
do_send(apns_env, apns_device_token, payload)
end
end
@spec do_send(environment(), String.t(), Map.t()) :: :ok | {:error, term()}
defp do_send(apns_env, apns_device_token, payload) do
2024-04-06 19:02:18 +00:00
with {:ok, body} <- Jason.encode(payload, pretty_print: false),
2024-04-12 20:16:46 +00:00
req <- make_request(apns_env, apns_device_token, body),
2024-04-06 19:02:18 +00:00
{:ok, resp} <- Finch.request(req, TuskerPush.Finch) do
2024-04-12 20:16:46 +00:00
handle_response(resp, apns_env, apns_device_token, payload)
2024-04-06 19:02:18 +00:00
else
{:error, %Finch.Error{reason: :connection_closed}} ->
Logger.warning("Apns Finch connection_closed, retrying in 1s")
2024-04-24 14:28:55 +00:00
OpenTelemetry.Tracer.add_event("Finch connection_closed", [])
Process.sleep(1000)
2024-04-12 20:16:46 +00:00
__MODULE__.send(apns_env, apns_device_token, payload)
{:error, %Finch.Error{reason: :disconnected}} ->
Logger.warning("Apns Finch disconnected, retrying in 1s")
2024-04-24 14:28:55 +00:00
OpenTelemetry.Tracer.add_event("Finch disconnected", [])
Process.sleep(1000)
2024-04-12 20:16:46 +00:00
__MODULE__.send(apns_env, apns_device_token, payload)
{:error, %Mint.TransportError{reason: :closed}} ->
Logger.warning("Apns Mint transport closed, retrying in 1s")
2024-04-24 14:28:55 +00:00
OpenTelemetry.Tracer.add_event("Mint transport closed", [])
Process.sleep(1000)
2024-04-12 20:16:46 +00:00
__MODULE__.send(apns_env, apns_device_token, payload)
2024-04-06 19:02:18 +00:00
{:error, reason} ->
{:error, reason}
end
end
2024-04-12 20:16:46 +00:00
@spec make_request(environment(), String.t(), binary()) :: Finch.Request.t()
defp make_request(apns_env, apns_device_token, body) do
2024-04-06 19:02:18 +00:00
bundle_id = Application.fetch_env!(:tusker_push, :apns)[:bundle_id]
2024-04-06 03:50:28 +00:00
headers = [
{"authorization", "bearer #{TuskerPush.Apns.Token.current()}"},
{"apns-push-type", "alert"},
2024-04-06 19:02:18 +00:00
{"apns-bundle-id", bundle_id},
{"apns-topic", bundle_id},
{"apns-expiration",
DateTime.utc_now() |> DateTime.add(1, :day) |> DateTime.to_unix() |> Integer.to_string()}
2024-04-06 03:50:28 +00:00
]
2024-04-06 19:02:18 +00:00
Finch.build(
:post,
2024-04-12 20:16:46 +00:00
"https://#{host(apns_env)}/3/device/#{apns_device_token}",
2024-04-06 19:02:18 +00:00
headers,
body
)
end
2024-04-12 20:16:46 +00:00
@spec handle_response(Finch.Response.t(), environment(), String.t(), Map.t()) ::
:ok | {:error, term()}
2024-04-06 19:02:18 +00:00
2024-04-12 20:16:46 +00:00
defp handle_response(resp, apns_env, apns_device_token, payload) do
maybe_log_unique_id(resp, apns_env)
2024-04-06 19:02:18 +00:00
if resp.status in 200..299 do
:ok
2024-04-06 03:50:28 +00:00
else
2024-04-06 19:02:18 +00:00
info =
case Jason.decode(resp.body) do
{:ok, data} ->
inspect(data)
{:error, _} ->
resp.body
end
case {resp.status, info} do
{403, %{reason: "ExpiredProviderToken"}} ->
Logger.warning("Expired provider token, retrying")
2024-04-24 14:28:55 +00:00
OpenTelemetry.Tracer.add_event("Expired provider token", [])
2024-04-12 20:16:46 +00:00
__MODULE__.send(apns_env, apns_device_token, payload)
{410, %{reason: "Unregistered"}} ->
Logger.warning("Device token unregistered")
2024-04-24 14:28:55 +00:00
OpenTelemetry.Tracer.set_status(:error, "Unregistered device token")
{:error, :device_token_unregistered}
_ ->
Logger.error("Received #{resp.status} with #{inspect(info)}")
2024-04-06 19:02:18 +00:00
2024-04-24 14:28:55 +00:00
OpenTelemetry.Tracer.set_attributes(
response_status: resp.status,
response_info: inspect(info)
)
OpenTelemetry.Tracer.set_status(:error, "Unexpected response")
{:error, "unexpected status #{resp.status}"}
end
2024-04-06 03:50:28 +00:00
end
end
2024-04-06 19:02:18 +00:00
@spec maybe_log_unique_id(Finch.Response.t(), :development | :production) :: :ok
defp maybe_log_unique_id(resp, :development) do
resp.headers
|> Enum.find(fn
{"apns-unique-id", _} -> true
_ -> false
end)
|> case do
{_, id} ->
Logger.debug("APNS unique id: #{id}")
2024-04-24 14:28:55 +00:00
OpenTelemetry.Tracer.add_event("APNS unique ID", unique_id: id)
2024-04-06 19:02:18 +00:00
_ ->
nil
end
:ok
end
defp maybe_log_unique_id(_resp, :production), do: :ok
2024-04-12 20:16:46 +00:00
@spec host(environment()) :: String.t()
2024-04-06 19:02:18 +00:00
2024-04-06 03:50:28 +00:00
def host(:development) do
"api.sandbox.push.apple.com"
end
def host(:production) do
"api.push.apple.com"
end
end