add preseem API client with test connection support

This commit is contained in:
Graham McIntire 2026-02-12 16:58:16 -06:00
parent e302b8e9c4
commit 06f822dd50
No known key found for this signature in database
2 changed files with 181 additions and 0 deletions

View file

@ -0,0 +1,103 @@
defmodule Towerops.Preseem.Client do
@moduledoc """
HTTP client for the Preseem Model API.
Uses Req with built-in test support via `Req.Test`.
"""
require Logger
@default_base_url "https://apidocs.preseem.com/model/v1"
@doc """
Tests the connection to the Preseem API by fetching account info.
Returns `{:ok, body}` on success, `{:error, reason}` on failure.
"""
def test_connection(api_key, opts \\ []) do
base_url = Keyword.get(opts, :base_url, @default_base_url)
case request(:get, "#{base_url}/account", api_key) 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: status, body: body}} ->
Logger.warning("Preseem API unexpected status #{status}: #{inspect(body)}")
{:error, {:unexpected_status, status}}
{:error, reason} ->
Logger.error("Preseem API connection error: #{inspect(reason)}")
{:error, reason}
end
end
@doc """
Lists all access points from the Preseem API.
Returns `{:ok, [access_point]}` on success.
"""
def list_access_points(api_key, opts \\ []) do
base_url = Keyword.get(opts, :base_url, @default_base_url)
case request(:get, "#{base_url}/access_points", api_key) do
{:ok, %{status: status, body: %{"data" => data}}} when status in 200..299 ->
{:ok, data}
{:ok, %{status: 401}} ->
{:error, :unauthorized}
{:ok, %{status: 403}} ->
{:error, :forbidden}
{:ok, %{status: status, body: body}} ->
{:error, {:unexpected_status, status, body}}
{:error, reason} ->
{:error, reason}
end
end
@doc """
Fetches metrics for a specific access point.
Returns `{:ok, metrics_map}` on success.
"""
def get_access_point_metrics(api_key, ap_id, opts \\ []) do
base_url = Keyword.get(opts, :base_url, @default_base_url)
case request(:get, "#{base_url}/access_points/#{ap_id}/metrics", api_key) do
{:ok, %{status: status, body: %{"data" => data}}} when status in 200..299 ->
{:ok, data}
{:ok, %{status: 401}} ->
{:error, :unauthorized}
{:ok, %{status: 403}} ->
{:error, :forbidden}
{:ok, %{status: status, body: body}} ->
{:error, {:unexpected_status, status, body}}
{:error, reason} ->
{:error, reason}
end
end
defp request(method, url, api_key) do
Req.request(
method: method,
url: url,
headers: [{"authorization", "Bearer #{api_key}"}, {"accept", "application/json"}],
plug: {Req.Test, __MODULE__}
)
rescue
exception ->
{:error, Exception.message(exception)}
end
end

View file

@ -0,0 +1,78 @@
defmodule Towerops.Preseem.ClientTest do
use ExUnit.Case, async: true
alias Towerops.Preseem.Client
describe "test_connection/1" do
test "returns ok when API responds with 200" do
Req.Test.stub(Client, fn conn ->
Req.Test.json(conn, %{"status" => "ok", "account" => "test-isp"})
end)
assert {:ok, %{"status" => "ok"}} = Client.test_connection("valid-api-key")
end
test "returns error for 401 unauthorized" do
Req.Test.stub(Client, fn conn ->
conn
|> Plug.Conn.put_status(401)
|> Req.Test.json(%{"error" => "unauthorized"})
end)
assert {:error, :unauthorized} = Client.test_connection("bad-key")
end
test "returns error for 403 forbidden" do
Req.Test.stub(Client, fn conn ->
conn
|> Plug.Conn.put_status(403)
|> Req.Test.json(%{"error" => "forbidden"})
end)
assert {:error, :forbidden} = Client.test_connection("restricted-key")
end
end
describe "list_access_points/1" do
test "returns list of access points on success" do
Req.Test.stub(Client, fn conn ->
Req.Test.json(conn, %{
"data" => [
%{"id" => "ap-1", "name" => "Tower1-AP1", "ip" => "10.0.0.1"},
%{"id" => "ap-2", "name" => "Tower1-AP2", "ip" => "10.0.0.2"}
]
})
end)
assert {:ok, [ap1, ap2]} = Client.list_access_points("valid-key")
assert ap1["id"] == "ap-1"
assert ap2["id"] == "ap-2"
end
test "returns error for 401" do
Req.Test.stub(Client, fn conn ->
conn |> Plug.Conn.put_status(401) |> Req.Test.json(%{"error" => "unauthorized"})
end)
assert {:error, :unauthorized} = Client.list_access_points("bad-key")
end
end
describe "get_access_point_metrics/2" do
test "returns metrics for an AP" do
Req.Test.stub(Client, fn conn ->
Req.Test.json(conn, %{
"data" => %{
"qoe_score" => 85.5,
"capacity_score" => 72.0,
"subscriber_count" => 42,
"p95_latency" => 18.3
}
})
end)
assert {:ok, metrics} = Client.get_access_point_metrics("valid-key", "ap-1")
assert metrics["qoe_score"] == 85.5
end
end
end