Packages

Elixir SDK for the Layr8 decentralized identity-native messaging network

Current section

Files

Jump to
layr8 lib layr8 rest.ex
Raw

lib/layr8/rest.ex

defmodule Layr8.REST do
@moduledoc """
Internal HTTP client for the Layr8 cloud-node REST API.
Uses `Req` for HTTP requests. Authentication is via `x-api-key` header.
Not intended for direct use; accessed through `Layr8.Credentials` and
`Layr8.Presentations`.
"""
@type t :: %__MODULE__{
base_url: String.t(),
api_key: String.t()
}
defstruct [:base_url, :api_key]
@doc """
Creates a new REST client.
## Parameters
- `base_url` — HTTP base URL of the cloud-node (e.g. `"https://node.example.com"`)
- `api_key` — API key for authentication
"""
@spec new(String.t(), String.t()) :: t()
def new(base_url, api_key) do
%__MODULE__{
base_url: String.trim_trailing(base_url, "/"),
api_key: api_key
}
end
@doc """
Issues a JSON POST request to `path` with `body` and returns the decoded response.
Returns `{:ok, map()}` on success or `{:error, term()}` on failure.
"""
@spec post(t(), String.t(), map()) :: {:ok, map()} | {:error, term()}
def post(%__MODULE__{} = client, path, body) do
url = client.base_url <> path
case Req.post(url, json: body, headers: auth_headers(client.api_key)) do
{:ok, %Req.Response{status: status, body: resp_body}} when status in 200..299 ->
{:ok, resp_body}
{:ok, %Req.Response{status: status, body: resp_body}} ->
{:error, parse_error(status, resp_body)}
{:error, reason} ->
{:error, reason}
end
end
@doc """
Issues a GET request to `path` and returns the decoded response.
Returns `{:ok, map()}` on success or `{:error, term()}` on failure.
"""
@spec get(t(), String.t()) :: {:ok, map()} | {:error, term()}
def get(%__MODULE__{} = client, path) do
url = client.base_url <> path
case Req.get(url, headers: auth_headers(client.api_key)) do
{:ok, %Req.Response{status: status, body: resp_body}} when status in 200..299 ->
{:ok, resp_body}
{:ok, %Req.Response{status: status, body: resp_body}} ->
{:error, parse_error(status, resp_body)}
{:error, reason} ->
{:error, reason}
end
end
# Build standard auth headers.
defp auth_headers(""), do: [{"content-type", "application/json"}]
defp auth_headers(api_key),
do: [{"content-type", "application/json"}, {"x-api-key", api_key}]
# Extract a human-readable error message from a failed response.
defp parse_error(status, body) when is_map(body) do
msg = Map.get(body, "error", inspect(body))
"REST API error #{status}: #{msg}"
end
defp parse_error(status, body) when is_binary(body) do
case Jason.decode(body) do
{:ok, %{"error" => msg}} -> "REST API error #{status}: #{msg}"
_ -> "REST API error #{status}: #{body}"
end
end
defp parse_error(status, body), do: "REST API error #{status}: #{inspect(body)}"
end