- Add retry: false to VISP Client HTTP requests - Add retry: false to ReleaseChecker GitHub API requests - Remove unused Plug.Conn import from RemoteIpLogger - Remove unused default parameter from req_get/2 Results: - VISP sync test: 7007ms → 113ms (62x faster) - ReleaseChecker test: 7004ms → 17ms (402x faster) - UserResetPasswordLive test: 1495ms → 284ms (5x faster) Req's default retry behavior (1s, 2s, 4s exponential backoff) was causing 7-second delays for HTTP 500/503 error responses in tests. For these clients, immediate failure is preferred over retries.
111 lines
2.8 KiB
Elixir
111 lines
2.8 KiB
Elixir
defmodule Towerops.Visp.Client do
|
|
@moduledoc """
|
|
REST client for the VISP API.
|
|
|
|
Uses Req with X-API-Key authentication and page-based pagination.
|
|
"""
|
|
|
|
require Logger
|
|
|
|
@base_url "https://api.visp.net"
|
|
|
|
@doc "Test the connection to VISP by fetching a single subscriber."
|
|
def test_connection(api_key) do
|
|
case get(api_key, "/api/v1/subscribers", %{"per_page" => "1"}) do
|
|
{:ok, _data} -> {:ok, %{}}
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
|
|
@doc "List all subscribers with automatic pagination."
|
|
def list_subscribers(api_key) do
|
|
paginate(api_key, "/api/v1/subscribers")
|
|
end
|
|
|
|
@doc "List all sites/towers with automatic pagination."
|
|
def list_sites(api_key) do
|
|
paginate(api_key, "/api/v1/sites")
|
|
end
|
|
|
|
@doc "List all equipment with automatic pagination."
|
|
def list_equipment(api_key) do
|
|
paginate(api_key, "/api/v1/equipment")
|
|
end
|
|
|
|
@doc "List all service plans."
|
|
def list_services(api_key) do
|
|
get(api_key, "/api/v1/services")
|
|
end
|
|
|
|
defp get(api_key, path, params \\ %{}) do
|
|
url = @base_url <> path
|
|
|
|
req_opts = [
|
|
method: :get,
|
|
url: url,
|
|
headers: [
|
|
{"x-api-key", api_key},
|
|
{"accept", "application/json"}
|
|
],
|
|
params: params,
|
|
# Disable retries - VISP errors should fail immediately
|
|
retry: false
|
|
]
|
|
|
|
req_opts =
|
|
if Application.get_env(:towerops, :env) == :test do
|
|
Keyword.put(req_opts, :plug, {Req.Test, __MODULE__})
|
|
else
|
|
req_opts
|
|
end
|
|
|
|
case Req.request(req_opts) do
|
|
{:ok, %{status: status, body: body}} when status in 200..299 ->
|
|
{:ok, body}
|
|
|
|
{:ok, %{status: 401}} ->
|
|
{:error, :unauthorized}
|
|
|
|
{:ok, %{status: 403}} ->
|
|
{:error, :forbidden}
|
|
|
|
{:ok, %{status: 429}} ->
|
|
{:error, {:rate_limited, 60}}
|
|
|
|
{:ok, %{status: status, body: body}} ->
|
|
Logger.warning("VISP API unexpected status #{status}: #{inspect(body)}")
|
|
{:error, {:unexpected_status, status}}
|
|
|
|
{:error, reason} ->
|
|
Logger.error("VISP API connection error: #{inspect(reason)}")
|
|
{:error, reason}
|
|
end
|
|
rescue
|
|
exception ->
|
|
{:error, Exception.message(exception)}
|
|
end
|
|
|
|
defp paginate(api_key, path, page \\ 1, acc \\ []) do
|
|
params = %{"page" => to_string(page), "per_page" => "100"}
|
|
|
|
case get(api_key, path, params) do
|
|
{:ok, %{"data" => data, "meta" => meta}} when is_list(data) ->
|
|
all = acc ++ data
|
|
|
|
if page < (meta["last_page"] || 1) do
|
|
paginate(api_key, path, page + 1, all)
|
|
else
|
|
{:ok, all}
|
|
end
|
|
|
|
{:ok, %{"data" => data}} when is_list(data) ->
|
|
{:ok, acc ++ data}
|
|
|
|
{:ok, data} when is_list(data) ->
|
|
{:ok, acc ++ data}
|
|
|
|
{:error, reason} ->
|
|
{:error, reason}
|
|
end
|
|
end
|
|
end
|