mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-12 02:35:42 +00:00
Add api specs (#217)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -26,6 +26,7 @@
|
||||
|
||||
.env
|
||||
*.local.env
|
||||
test/manual/.auto*
|
||||
|
||||
.direnv/
|
||||
.cache/
|
||||
|
||||
BIN
assets/static/images/news/03-05-api/swagger-ui.png
Executable file
BIN
assets/static/images/news/03-05-api/swagger-ui.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 79 KiB |
32
lib/wanderer_app_web/api_spec.ex
Normal file
32
lib/wanderer_app_web/api_spec.ex
Normal file
@@ -0,0 +1,32 @@
|
||||
defmodule WandererAppWeb.ApiSpec do
|
||||
@behaviour OpenApiSpex.OpenApi
|
||||
|
||||
alias OpenApiSpex.{OpenApi, Info, Paths, Components, SecurityScheme, Server}
|
||||
alias WandererAppWeb.{Endpoint, Router}
|
||||
|
||||
@impl OpenApiSpex.OpenApi
|
||||
def spec do
|
||||
%OpenApi{
|
||||
info: %Info{
|
||||
title: "WandererApp API",
|
||||
version: "1.0.0",
|
||||
description: "API documentation for WandererApp"
|
||||
},
|
||||
servers: [
|
||||
Server.from_endpoint(Endpoint)
|
||||
],
|
||||
paths: Paths.from_router(Router),
|
||||
components: %Components{
|
||||
securitySchemes: %{
|
||||
"bearerAuth" => %SecurityScheme{
|
||||
type: "http",
|
||||
scheme: "bearer",
|
||||
bearerFormat: "JWT"
|
||||
}
|
||||
}
|
||||
},
|
||||
security: [%{"bearerAuth" => []}]
|
||||
}
|
||||
|> OpenApiSpex.resolve_schema_modules()
|
||||
end
|
||||
end
|
||||
@@ -7,26 +7,247 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
- POST /api/map/acls (create ACL)
|
||||
- GET /api/acls/:id (show ACL)
|
||||
- PUT /api/acls/:id (update ACL)
|
||||
|
||||
ACL members are managed via a separate controller.
|
||||
"""
|
||||
|
||||
use WandererAppWeb, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
|
||||
alias WandererApp.Api.{AccessList, Character}
|
||||
alias WandererAppWeb.UtilAPIController, as: Util
|
||||
import Ash.Query
|
||||
require Logger
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Inline Schemas for OpenApiSpex
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
# Used in operation :index => the response "List of ACLs"
|
||||
@acl_index_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :array,
|
||||
items: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
description: %OpenApiSpex.Schema{type: :string},
|
||||
owner_eve_id: %OpenApiSpex.Schema{type: :string},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time}
|
||||
},
|
||||
required: ["id", "name"]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
# Used in operation :create => the request body "ACL parameters"
|
||||
@acl_create_request_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
acl: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
owner_eve_id: %OpenApiSpex.Schema{
|
||||
type: :string,
|
||||
description: "EVE character ID of the owner (must match an existing character)"
|
||||
},
|
||||
name: %OpenApiSpex.Schema{
|
||||
type: :string,
|
||||
description: "Name of the access list"
|
||||
},
|
||||
description: %OpenApiSpex.Schema{
|
||||
type: :string,
|
||||
description: "Optional description of the access list"
|
||||
}
|
||||
},
|
||||
required: ["owner_eve_id", "name"],
|
||||
example: %{
|
||||
"owner_eve_id" => "2112073677",
|
||||
"name" => "My Access List",
|
||||
"description" => "Optional description"
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["acl"]
|
||||
}
|
||||
|
||||
# Used in operation :create => the response "Created ACL"
|
||||
@acl_create_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
description: %OpenApiSpex.Schema{type: :string},
|
||||
owner_id: %OpenApiSpex.Schema{type: :string},
|
||||
api_key: %OpenApiSpex.Schema{type: :string},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time}
|
||||
},
|
||||
required: ["id", "name"]
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
# Used in operation :show => the response "ACL details"
|
||||
@acl_show_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
description: %OpenApiSpex.Schema{type: :string},
|
||||
owner_id: %OpenApiSpex.Schema{type: :string},
|
||||
api_key: %OpenApiSpex.Schema{type: :string},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
members: %OpenApiSpex.Schema{
|
||||
type: :array,
|
||||
items: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
role: %OpenApiSpex.Schema{type: :string},
|
||||
eve_character_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_corporation_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_alliance_id: %OpenApiSpex.Schema{type: :string},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time}
|
||||
},
|
||||
required: ["id", "name", "role"]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["id", "name"]
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
# Used in operation :update => the request body "ACL update payload"
|
||||
@acl_update_request_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
acl: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
description: %OpenApiSpex.Schema{type: :string}
|
||||
}
|
||||
# If "name" is truly required, add it to required: ["name"] here
|
||||
}
|
||||
},
|
||||
required: ["acl"]
|
||||
}
|
||||
|
||||
# Used in operation :update => the response "Updated ACL"
|
||||
@acl_update_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
description: %OpenApiSpex.Schema{type: :string},
|
||||
owner_id: %OpenApiSpex.Schema{type: :string},
|
||||
api_key: %OpenApiSpex.Schema{type: :string},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
members: %OpenApiSpex.Schema{
|
||||
type: :array,
|
||||
items: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
role: %OpenApiSpex.Schema{type: :string},
|
||||
eve_character_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_corporation_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_alliance_id: %OpenApiSpex.Schema{type: :string},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time}
|
||||
},
|
||||
required: ["id", "name", "role"]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["id", "name"]
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# ENDPOINTS
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
@doc """
|
||||
GET /api/map/acls?map_id=... or ?slug=...
|
||||
|
||||
Lists the ACLs for a given map.
|
||||
"""
|
||||
@spec index(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :index,
|
||||
summary: "List ACLs for a Map",
|
||||
description: "Lists the ACLs for a given map. Requires either 'map_id' or 'slug' as a query parameter to identify the map.",
|
||||
parameters: [
|
||||
map_id: [
|
||||
in: :query,
|
||||
description: "Map identifier (UUID) - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
],
|
||||
slug: [
|
||||
in: :query,
|
||||
description: "Map slug - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "map-name"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {
|
||||
"List of ACLs",
|
||||
"application/json",
|
||||
@acl_index_response_schema
|
||||
},
|
||||
bad_request: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Must provide either ?map_id=UUID or ?slug=SLUG as a query parameter"
|
||||
}
|
||||
}},
|
||||
not_found: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Map not found. Please provide a valid map_id or slug as a query parameter."
|
||||
}
|
||||
}}
|
||||
]
|
||||
def index(conn, params) do
|
||||
case Util.fetch_map_id(params) do
|
||||
{:ok, map_identifier} ->
|
||||
with {:ok, map} <- get_map(map_identifier),
|
||||
# Load ACLs and each ACL's :owner in a single pass:
|
||||
{:ok, loaded_map} <- Ash.load(map, acls: [:owner]) do
|
||||
acls = loaded_map.acls || []
|
||||
json(conn, %{data: Enum.map(acls, &acl_to_list_json/1)})
|
||||
@@ -34,7 +255,7 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
{:error, :map_not_found} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Map not found"})
|
||||
|> json(%{error: "Map not found. Please provide a valid map_id or slug as a query parameter."})
|
||||
|
||||
{:error, error} ->
|
||||
conn
|
||||
@@ -42,10 +263,10 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
|> json(%{error: inspect(error)})
|
||||
end
|
||||
|
||||
{:error, msg} ->
|
||||
{:error, _msg} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: msg})
|
||||
|> json(%{error: "Must provide either ?map_id=UUID or ?slug=SLUG as a query parameter"})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -54,6 +275,50 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
|
||||
Creates a new ACL for a map.
|
||||
"""
|
||||
@spec create(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :create,
|
||||
summary: "Create a new ACL",
|
||||
description: "Creates a new ACL for a map. Requires either 'map_id' or 'slug' as a query parameter to identify the map.",
|
||||
parameters: [
|
||||
map_id: [
|
||||
in: :query,
|
||||
description: "Map identifier (UUID) - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
],
|
||||
slug: [
|
||||
in: :query,
|
||||
description: "Map slug - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "map-name"
|
||||
]
|
||||
],
|
||||
request_body: {"Access List parameters", "application/json", @acl_create_request_schema},
|
||||
responses: [
|
||||
ok: {"Access List", "application/json", @acl_create_response_schema},
|
||||
bad_request: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Must provide either ?map_id=UUID or ?slug=SLUG as a query parameter"
|
||||
}
|
||||
}},
|
||||
not_found: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Map not found. Please provide a valid map_id or slug as a query parameter."
|
||||
}
|
||||
}}
|
||||
]
|
||||
def create(conn, params) do
|
||||
with {:ok, map_identifier} <- Util.fetch_map_id(params),
|
||||
{:ok, map} <- get_map(map_identifier),
|
||||
@@ -71,6 +336,16 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
{:ok, _updated_map} <- associate_acl_with_map(map, new_acl) do
|
||||
json(conn, %{data: acl_to_json(new_acl)})
|
||||
else
|
||||
{:error, :map_not_found} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Map not found. Please provide a valid map_id or slug as a query parameter."})
|
||||
|
||||
{:error, "Must provide either ?map_id=UUID or ?slug=SLUG"} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: "Must provide either ?map_id=UUID or ?slug=SLUG as a query parameter"})
|
||||
|
||||
nil ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
@@ -79,9 +354,13 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
{:error, "owner_eve_id does not match any existing character"} = error ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: inspect(error)})
|
||||
|> json(%{error: "Character not found: The provided owner_eve_id does not match any existing character"})
|
||||
|
||||
%{} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: "Missing required 'acl' object in request body"})
|
||||
|
||||
# For any other error, also a bad request—adjust if you want a different code
|
||||
error ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
@@ -94,6 +373,46 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
|
||||
Shows a specific ACL (with its members).
|
||||
"""
|
||||
@spec show(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :show,
|
||||
summary: "Get ACL details",
|
||||
description: "Retrieves details for a specific ACL by its ID.",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "ACL identifier (UUID)",
|
||||
type: :string,
|
||||
required: true,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {
|
||||
"ACL details",
|
||||
"application/json",
|
||||
@acl_show_response_schema
|
||||
},
|
||||
not_found: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "ACL not found"
|
||||
}
|
||||
}},
|
||||
internal_server_error: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Failed to load ACL members: reason"
|
||||
}
|
||||
}}
|
||||
]
|
||||
def show(conn, %{"id" => id}) do
|
||||
query =
|
||||
AccessList
|
||||
@@ -102,7 +421,6 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
|
||||
case WandererApp.Api.read(query) do
|
||||
{:ok, [acl]} ->
|
||||
# We load members for a single ACL
|
||||
case Ash.load(acl, :members) do
|
||||
{:ok, loaded_acl} ->
|
||||
json(conn, %{data: acl_to_json(loaded_acl)})
|
||||
@@ -130,6 +448,51 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
|
||||
Updates an ACL.
|
||||
"""
|
||||
@spec update(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :update,
|
||||
summary: "Update an ACL",
|
||||
description: "Updates an existing ACL by its ID.",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :path,
|
||||
description: "ACL identifier (UUID)",
|
||||
type: :string,
|
||||
required: true,
|
||||
example: "00000000-0000-0000-0000-000000000000"
|
||||
]
|
||||
],
|
||||
request_body: {
|
||||
"ACL update payload",
|
||||
"application/json",
|
||||
@acl_update_request_schema
|
||||
},
|
||||
responses: [
|
||||
ok: {
|
||||
"Updated ACL",
|
||||
"application/json",
|
||||
@acl_update_response_schema
|
||||
},
|
||||
bad_request: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Failed to update ACL: invalid parameters"
|
||||
}
|
||||
}},
|
||||
not_found: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "ACL not found"
|
||||
}
|
||||
}}
|
||||
]
|
||||
def update(conn, %{"id" => id, "acl" => acl_params}) do
|
||||
with {:ok, acl} <- AccessList.by_id(id),
|
||||
{:ok, updated_acl} <- AccessList.update(acl, acl_params),
|
||||
@@ -147,9 +510,10 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
# Private / Helper Functions
|
||||
# ---------------------------------------------------------------------------
|
||||
defp get_map(map_identifier) do
|
||||
# If your WandererApp.Api.Map.by_id/1 returns :map_not_found or
|
||||
# returns {:ok, map}/{:error, ...}, you can handle that here
|
||||
WandererApp.Api.Map.by_id(map_identifier)
|
||||
case WandererApp.Api.Map.by_id(map_identifier) do
|
||||
{:ok, map} -> {:ok, map}
|
||||
{:error, _} -> {:error, :map_not_found}
|
||||
end
|
||||
end
|
||||
|
||||
defp acl_to_json(acl) do
|
||||
@@ -173,7 +537,6 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
end
|
||||
|
||||
defp acl_to_list_json(acl) do
|
||||
# Because we loaded :owner for each ACL in index/2, we can reference it here
|
||||
owner_eve_id =
|
||||
case acl.owner do
|
||||
%Character{eve_id: eid} -> eid
|
||||
@@ -191,17 +554,22 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
end
|
||||
|
||||
defp member_to_json(member) do
|
||||
%{
|
||||
base = %{
|
||||
id: member.id,
|
||||
name: member.name,
|
||||
role: member.role,
|
||||
eve_character_id: member.eve_character_id,
|
||||
inserted_at: member.inserted_at,
|
||||
updated_at: member.updated_at
|
||||
}
|
||||
|
||||
cond do
|
||||
member.eve_character_id -> Map.put(base, :eve_character_id, member.eve_character_id)
|
||||
member.eve_corporation_id -> Map.put(base, :eve_corporation_id, member.eve_corporation_id)
|
||||
member.eve_alliance_id -> Map.put(base, :eve_alliance_id, member.eve_alliance_id)
|
||||
true -> base
|
||||
end
|
||||
end
|
||||
|
||||
# Helper to find a character by external EVE id.
|
||||
defp find_character_by_eve_id(eve_id) do
|
||||
query =
|
||||
Character
|
||||
@@ -225,8 +593,16 @@ defmodule WandererAppWeb.MapAccessListAPIController do
|
||||
with {:ok, api_map} <- WandererApp.Api.Map.by_id(map.id),
|
||||
{:ok, loaded_map} <- Ash.load(api_map, :acls) do
|
||||
new_acl_id = if is_binary(new_acl), do: new_acl, else: new_acl.id
|
||||
current_acls = loaded_map.acls || []
|
||||
updated_acls = current_acls ++ [new_acl_id]
|
||||
|
||||
# Extract IDs from current ACLs to ensure we're working with UUIDs only
|
||||
current_acl_ids = loaded_map.acls
|
||||
|> Kernel.||([])
|
||||
|> Enum.map(fn
|
||||
acl when is_binary(acl) -> acl
|
||||
acl -> acl.id
|
||||
end)
|
||||
|
||||
updated_acls = current_acl_ids ++ [new_acl_id]
|
||||
|
||||
case WandererApp.Api.Map.update_acls(loaded_map, %{acls: updated_acls}) do
|
||||
{:ok, updated_map} ->
|
||||
|
||||
@@ -1,23 +1,132 @@
|
||||
defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
@moduledoc """
|
||||
Handles creation, role updates, and deletion of individual ACL members.
|
||||
|
||||
This controller supports creation of members by accepting one of the following keys:
|
||||
- "eve_character_id"
|
||||
- "eve_corporation_id"
|
||||
- "eve_alliance_id"
|
||||
|
||||
For corporation and alliance members, roles "admin" and "manager" are disallowed.
|
||||
"""
|
||||
|
||||
use WandererAppWeb, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
|
||||
alias WandererApp.Api.AccessListMember
|
||||
import Ash.Query
|
||||
require Logger
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# Inline Schemas
|
||||
# ------------------------------------------------------------------------
|
||||
@acl_member_create_request_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
member: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
eve_character_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_corporation_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_alliance_id: %OpenApiSpex.Schema{type: :string},
|
||||
role: %OpenApiSpex.Schema{type: :string}
|
||||
}
|
||||
# no 'required' fields if you truly allow any of them
|
||||
}
|
||||
},
|
||||
required: ["member"]
|
||||
}
|
||||
|
||||
@acl_member_create_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
role: %OpenApiSpex.Schema{type: :string},
|
||||
eve_character_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_corporation_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_alliance_id: %OpenApiSpex.Schema{type: :string},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time}
|
||||
},
|
||||
required: ["id", "name", "role"]
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
@acl_member_update_request_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
member: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
role: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["role"]
|
||||
}
|
||||
},
|
||||
required: ["member"]
|
||||
}
|
||||
|
||||
@acl_member_update_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
role: %OpenApiSpex.Schema{type: :string},
|
||||
eve_character_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_corporation_id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_alliance_id: %OpenApiSpex.Schema{type: :string},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time}
|
||||
},
|
||||
required: ["id", "name", "role"]
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
@acl_member_delete_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
ok: %OpenApiSpex.Schema{type: :boolean}
|
||||
},
|
||||
required: ["ok"]
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------
|
||||
# ENDPOINTS
|
||||
# ------------------------------------------------------------------------
|
||||
|
||||
@doc """
|
||||
POST /api/acls/:acl_id/members
|
||||
|
||||
Creates a new ACL member.
|
||||
"""
|
||||
@spec create(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :create,
|
||||
summary: "Create ACL Member",
|
||||
description: "Creates a new ACL member for a given ACL.",
|
||||
parameters: [
|
||||
acl_id: [
|
||||
in: :path,
|
||||
description: "Access List ID",
|
||||
type: :string,
|
||||
required: true
|
||||
]
|
||||
],
|
||||
request_body: {
|
||||
"ACL Member parameters",
|
||||
"application/json",
|
||||
@acl_member_create_request_schema
|
||||
},
|
||||
responses: [
|
||||
ok: {
|
||||
"Created ACL Member",
|
||||
"application/json",
|
||||
@acl_member_create_response_schema
|
||||
}
|
||||
]
|
||||
def create(conn, %{"acl_id" => acl_id, "member" => member_params}) do
|
||||
chosen =
|
||||
cond do
|
||||
@@ -44,7 +153,7 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
else
|
||||
{key, type} = chosen
|
||||
raw_id = Map.get(member_params, key)
|
||||
id_str = to_string(raw_id) # handle string/integer input
|
||||
id_str = to_string(raw_id)
|
||||
role = Map.get(member_params, "role", "viewer")
|
||||
|
||||
if type in ["corporation", "alliance"] and role in ["admin", "manager"] do
|
||||
@@ -93,13 +202,44 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
|
||||
@doc """
|
||||
PUT /api/acls/:acl_id/members/:member_id
|
||||
|
||||
Updates the role of an ACL member.
|
||||
"""
|
||||
@spec update_role(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :update_role,
|
||||
summary: "Update ACL Member Role",
|
||||
description: "Updates the role of an ACL member identified by ACL ID and member external ID.",
|
||||
parameters: [
|
||||
acl_id: [
|
||||
in: :path,
|
||||
description: "Access List ID",
|
||||
type: :string,
|
||||
required: true
|
||||
],
|
||||
member_id: [
|
||||
in: :path,
|
||||
description: "Member external ID",
|
||||
type: :string,
|
||||
required: true
|
||||
]
|
||||
],
|
||||
request_body: {
|
||||
"ACL Member update payload",
|
||||
"application/json",
|
||||
@acl_member_update_request_schema
|
||||
},
|
||||
responses: [
|
||||
ok: {
|
||||
"Updated ACL Member",
|
||||
"application/json",
|
||||
@acl_member_update_response_schema
|
||||
}
|
||||
]
|
||||
def update_role(conn, %{
|
||||
"acl_id" => acl_id,
|
||||
"member_id" => external_id,
|
||||
"member" => member_params
|
||||
}) do
|
||||
# Convert external_id to string if you expect it may come in as integer
|
||||
external_id_str = to_string(external_id)
|
||||
|
||||
membership_query =
|
||||
@@ -157,7 +297,34 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
|
||||
@doc """
|
||||
DELETE /api/acls/:acl_id/members/:member_id
|
||||
|
||||
Deletes an ACL member.
|
||||
"""
|
||||
@spec delete(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :delete,
|
||||
summary: "Delete ACL Member",
|
||||
description: "Deletes an ACL member identified by ACL ID and member external ID.",
|
||||
parameters: [
|
||||
acl_id: [
|
||||
in: :path,
|
||||
description: "Access List ID",
|
||||
type: :string,
|
||||
required: true
|
||||
],
|
||||
member_id: [
|
||||
in: :path,
|
||||
description: "Member external ID",
|
||||
type: :string,
|
||||
required: true
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {
|
||||
"ACL Member deletion confirmation",
|
||||
"application/json",
|
||||
@acl_member_delete_response_schema
|
||||
}
|
||||
]
|
||||
def delete(conn, %{"acl_id" => acl_id, "member_id" => external_id}) do
|
||||
external_id_str = to_string(external_id)
|
||||
|
||||
@@ -204,6 +371,9 @@ defmodule WandererAppWeb.AccessListMemberAPIController do
|
||||
id: member.id,
|
||||
name: member.name,
|
||||
role: member.role,
|
||||
eve_character_id: member.eve_character_id,
|
||||
eve_corporation_id: member.eve_corporation_id,
|
||||
eve_alliance_id: member.eve_alliance_id,
|
||||
inserted_at: member.inserted_at,
|
||||
updated_at: member.updated_at
|
||||
}
|
||||
|
||||
@@ -1,20 +1,47 @@
|
||||
defmodule WandererAppWeb.CharactersAPIController do
|
||||
@moduledoc """
|
||||
Exposes an endpoint for listing ALL characters in the database
|
||||
|
||||
Endpoint:
|
||||
GET /api/characters
|
||||
"""
|
||||
|
||||
use WandererAppWeb, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
alias WandererApp.Api.Character
|
||||
|
||||
@characters_index_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :array,
|
||||
items: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
corporation_name: %OpenApiSpex.Schema{type: :string},
|
||||
alliance_name: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["id", "eve_id", "name"]
|
||||
}
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
@doc """
|
||||
GET /api/characters
|
||||
|
||||
Lists ALL characters in the database
|
||||
Returns an array of objects, each with `id`, `eve_id`, `name`, etc.
|
||||
"""
|
||||
@spec index(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :index,
|
||||
summary: "List Characters",
|
||||
description: "Lists ALL characters in the database.",
|
||||
responses: [
|
||||
ok: {
|
||||
"List of characters",
|
||||
"application/json",
|
||||
@characters_index_response_schema
|
||||
}
|
||||
]
|
||||
def index(conn, _params) do
|
||||
case WandererApp.Api.read(Character) do
|
||||
{:ok, characters} ->
|
||||
|
||||
@@ -1,17 +1,64 @@
|
||||
defmodule WandererAppWeb.CommonAPIController do
|
||||
use WandererAppWeb, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
|
||||
alias WandererApp.CachedInfo
|
||||
alias WandererAppWeb.UtilAPIController, as: Util
|
||||
|
||||
@system_static_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
solar_system_id: %OpenApiSpex.Schema{type: :integer},
|
||||
region_id: %OpenApiSpex.Schema{type: :integer},
|
||||
constellation_id: %OpenApiSpex.Schema{type: :integer},
|
||||
solar_system_name: %OpenApiSpex.Schema{type: :string},
|
||||
solar_system_name_lc: %OpenApiSpex.Schema{type: :string},
|
||||
constellation_name: %OpenApiSpex.Schema{type: :string},
|
||||
region_name: %OpenApiSpex.Schema{type: :string},
|
||||
system_class: %OpenApiSpex.Schema{type: :integer},
|
||||
security: %OpenApiSpex.Schema{type: :string},
|
||||
type_description: %OpenApiSpex.Schema{type: :string},
|
||||
class_title: %OpenApiSpex.Schema{type: :string},
|
||||
is_shattered: %OpenApiSpex.Schema{type: :boolean},
|
||||
effect_name: %OpenApiSpex.Schema{type: :string},
|
||||
effect_power: %OpenApiSpex.Schema{type: :integer},
|
||||
statics: %OpenApiSpex.Schema{type: :array, items: %OpenApiSpex.Schema{type: :string}},
|
||||
wandering: %OpenApiSpex.Schema{type: :array, items: %OpenApiSpex.Schema{type: :string}},
|
||||
triglavian_invasion_status: %OpenApiSpex.Schema{type: :string},
|
||||
sun_type_id: %OpenApiSpex.Schema{type: :integer}
|
||||
},
|
||||
required: ["solar_system_id", "solar_system_name"]
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
@doc """
|
||||
GET /api/common/system_static?id=<solar_system_id>
|
||||
|
||||
Requires 'id' (the solar_system_id).
|
||||
|
||||
Example:
|
||||
GET /api/common/system_static?id=31002229
|
||||
GET /api/common/system-static-info?id=<solar_system_id>
|
||||
"""
|
||||
@spec show_system_static(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :show_system_static,
|
||||
summary: "Get System Static Information",
|
||||
description: "Retrieves static information for a given solar system.",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :query,
|
||||
description: "Solar system ID",
|
||||
type: :string,
|
||||
example: "30000142",
|
||||
required: true
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {
|
||||
"System static info",
|
||||
"application/json",
|
||||
@system_static_response_schema
|
||||
}
|
||||
]
|
||||
def show_system_static(conn, params) do
|
||||
with {:ok, solar_system_str} <- Util.require_param(params, "id"),
|
||||
{:ok, solar_system_id} <- Util.parse_int(solar_system_str) do
|
||||
@@ -33,10 +80,6 @@ defmodule WandererAppWeb.CommonAPIController do
|
||||
end
|
||||
end
|
||||
|
||||
# ----------------------------------------------
|
||||
# Private helpers
|
||||
# ----------------------------------------------
|
||||
|
||||
defp static_system_to_json(system) do
|
||||
system
|
||||
|> Map.take([
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
defmodule WandererAppWeb.MapAPIController do
|
||||
use WandererAppWeb, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
|
||||
import Ash.Query, only: [filter: 2]
|
||||
require Logger
|
||||
|
||||
alias WandererApp.Api
|
||||
alias WandererApp.Api.Character
|
||||
alias WandererApp.Api.MapSolarSystem
|
||||
alias WandererApp.MapSystemRepo
|
||||
alias WandererApp.MapCharacterSettingsRepo
|
||||
|
||||
@@ -13,6 +15,166 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
|
||||
alias WandererAppWeb.UtilAPIController, as: Util
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Inline Schemas
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
@map_system_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
map_id: %OpenApiSpex.Schema{type: :string},
|
||||
solar_system_id: %OpenApiSpex.Schema{type: :integer},
|
||||
original_name: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
custom_name: %OpenApiSpex.Schema{type: :string},
|
||||
temporary_name: %OpenApiSpex.Schema{type: :string},
|
||||
description: %OpenApiSpex.Schema{type: :string},
|
||||
tag: %OpenApiSpex.Schema{type: :string},
|
||||
labels: %OpenApiSpex.Schema{type: :array, items: %OpenApiSpex.Schema{type: :string}},
|
||||
locked: %OpenApiSpex.Schema{type: :boolean},
|
||||
visible: %OpenApiSpex.Schema{type: :boolean},
|
||||
status: %OpenApiSpex.Schema{type: :string},
|
||||
position_x: %OpenApiSpex.Schema{type: :integer},
|
||||
position_y: %OpenApiSpex.Schema{type: :integer},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time}
|
||||
},
|
||||
required: ["id", "solar_system_id", "original_name", "name"]
|
||||
}
|
||||
|
||||
@list_map_systems_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :array,
|
||||
items: @map_system_schema
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
@show_map_system_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: @map_system_schema
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
# For operation :tracked_characters_with_info
|
||||
@character_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
eve_id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
corporation_id: %OpenApiSpex.Schema{type: :string},
|
||||
corporation_name: %OpenApiSpex.Schema{type: :string},
|
||||
corporation_ticker: %OpenApiSpex.Schema{type: :string},
|
||||
alliance_id: %OpenApiSpex.Schema{type: :string},
|
||||
alliance_name: %OpenApiSpex.Schema{type: :string},
|
||||
alliance_ticker: %OpenApiSpex.Schema{type: :string},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time}
|
||||
},
|
||||
required: ["id", "eve_id", "name"]
|
||||
}
|
||||
|
||||
@tracked_char_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %OpenApiSpex.Schema{type: :string},
|
||||
map_id: %OpenApiSpex.Schema{type: :string},
|
||||
character_id: %OpenApiSpex.Schema{type: :string},
|
||||
tracked: %OpenApiSpex.Schema{type: :boolean},
|
||||
inserted_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
updated_at: %OpenApiSpex.Schema{type: :string, format: :date_time},
|
||||
character: @character_schema
|
||||
},
|
||||
required: ["id", "map_id", "character_id", "tracked"]
|
||||
}
|
||||
|
||||
@tracked_characters_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :array,
|
||||
items: @tracked_char_schema
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
# For operation :show_structure_timers
|
||||
@structure_timer_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
system_id: %OpenApiSpex.Schema{type: :string},
|
||||
solar_system_name: %OpenApiSpex.Schema{type: :string},
|
||||
solar_system_id: %OpenApiSpex.Schema{type: :integer},
|
||||
structure_type_id: %OpenApiSpex.Schema{type: :integer},
|
||||
structure_type: %OpenApiSpex.Schema{type: :string},
|
||||
character_eve_id: %OpenApiSpex.Schema{type: :string},
|
||||
name: %OpenApiSpex.Schema{type: :string},
|
||||
notes: %OpenApiSpex.Schema{type: :string},
|
||||
owner_name: %OpenApiSpex.Schema{type: :string},
|
||||
owner_ticker: %OpenApiSpex.Schema{type: :string},
|
||||
owner_id: %OpenApiSpex.Schema{type: :string},
|
||||
status: %OpenApiSpex.Schema{type: :string},
|
||||
end_time: %OpenApiSpex.Schema{type: :string, format: :date_time}
|
||||
},
|
||||
required: ["system_id", "solar_system_id", "name", "status"]
|
||||
}
|
||||
|
||||
@structure_timers_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :array,
|
||||
items: @structure_timer_schema
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
# For operation :list_systems_kills
|
||||
@kill_item_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
description: "Kill detail object",
|
||||
properties: %{
|
||||
kill_id: %OpenApiSpex.Schema{type: :integer, description: "Unique identifier for the kill"},
|
||||
kill_time: %OpenApiSpex.Schema{type: :string, format: :date_time, description: "Time when the kill occurred"},
|
||||
victim_id: %OpenApiSpex.Schema{type: :integer, description: "ID of the victim character"},
|
||||
victim_name: %OpenApiSpex.Schema{type: :string, description: "Name of the victim character"},
|
||||
ship_type_id: %OpenApiSpex.Schema{type: :integer, description: "Type ID of the destroyed ship"},
|
||||
ship_name: %OpenApiSpex.Schema{type: :string, description: "Name of the destroyed ship"}
|
||||
}
|
||||
}
|
||||
|
||||
@system_kills_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
solar_system_id: %OpenApiSpex.Schema{type: :integer},
|
||||
kills: %OpenApiSpex.Schema{
|
||||
type: :array,
|
||||
items: @kill_item_schema
|
||||
}
|
||||
},
|
||||
required: ["solar_system_id", "kills"]
|
||||
}
|
||||
|
||||
@systems_kills_response_schema %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
data: %OpenApiSpex.Schema{
|
||||
type: :array,
|
||||
items: @system_kills_schema
|
||||
}
|
||||
},
|
||||
required: ["data"]
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# MAP endpoints
|
||||
# -----------------------------------------------------------------
|
||||
@@ -28,6 +190,43 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
GET /api/map/systems?map_id=466e922b-e758-485e-9b86-afae06b88363
|
||||
GET /api/map/systems?slug=my-unique-wormhole-map
|
||||
"""
|
||||
@spec list_systems(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :list_systems,
|
||||
summary: "List Map Systems",
|
||||
description: "Lists all visible systems for a map. Requires either 'map_id' or 'slug' as a query parameter to identify the map.",
|
||||
parameters: [
|
||||
map_id: [
|
||||
in: :query,
|
||||
description: "Map identifier (UUID) - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: ""
|
||||
],
|
||||
slug: [
|
||||
in: :query,
|
||||
description: "Map slug - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "map-name"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {
|
||||
"List of map systems",
|
||||
"application/json",
|
||||
@list_map_systems_response_schema
|
||||
},
|
||||
bad_request: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Must provide either ?map_id=UUID or ?slug=SLUG"
|
||||
}
|
||||
}}
|
||||
]
|
||||
def list_systems(conn, params) do
|
||||
with {:ok, map_id} <- Util.fetch_map_id(params),
|
||||
{:ok, systems} <- MapSystemRepo.get_visible_by_map(map_id) do
|
||||
@@ -56,6 +255,60 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
GET /api/map/system?id=31002229&map_id=466e922b-e758-485e-9b86-afae06b88363
|
||||
GET /api/map/system?id=31002229&slug=my-unique-wormhole-map
|
||||
"""
|
||||
@spec show_system(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :show_system,
|
||||
summary: "Show Map System",
|
||||
description: "Retrieves details for a specific map system (by solar_system_id + map). Requires either 'map_id' or 'slug' as a query parameter to identify the map.",
|
||||
parameters: [
|
||||
id: [
|
||||
in: :query,
|
||||
description: "System ID",
|
||||
type: :string,
|
||||
required: true,
|
||||
example: "30000142"
|
||||
],
|
||||
map_id: [
|
||||
in: :query,
|
||||
description: "Map identifier (UUID) - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: ""
|
||||
],
|
||||
slug: [
|
||||
in: :query,
|
||||
description: "Map slug - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "map-name"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {
|
||||
"Map system details",
|
||||
"application/json",
|
||||
@show_map_system_response_schema
|
||||
},
|
||||
bad_request: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Must provide either ?map_id=UUID or ?slug=SLUG as a query parameter"
|
||||
}
|
||||
}},
|
||||
not_found: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "System not found"
|
||||
}
|
||||
}}
|
||||
]
|
||||
def show_system(conn, params) do
|
||||
with {:ok, solar_system_str} <- Util.require_param(params, "id"),
|
||||
{:ok, solar_system_id} <- Util.parse_int(solar_system_str),
|
||||
@@ -90,6 +343,43 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
|
||||
Returns a list of tracked records, plus their fully-loaded `character` data.
|
||||
"""
|
||||
@spec tracked_characters_with_info(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :tracked_characters_with_info,
|
||||
summary: "List Tracked Characters with Info",
|
||||
description: "Lists all tracked characters for a map with their information. Requires either 'map_id' or 'slug' as a query parameter to identify the map.",
|
||||
parameters: [
|
||||
map_id: [
|
||||
in: :query,
|
||||
description: "Map identifier (UUID) - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: ""
|
||||
],
|
||||
slug: [
|
||||
in: :query,
|
||||
description: "Map slug - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "map-name"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {
|
||||
"List of tracked characters",
|
||||
"application/json",
|
||||
@tracked_characters_response_schema
|
||||
},
|
||||
bad_request: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Must provide either ?map_id=UUID or ?slug=SLUG"
|
||||
}
|
||||
}}
|
||||
]
|
||||
def tracked_characters_with_info(conn, params) do
|
||||
with {:ok, map_id} <- Util.fetch_map_id(params),
|
||||
{:ok, settings_list} <- get_tracked_by_map_ids(map_id),
|
||||
@@ -151,6 +441,50 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
GET /api/map/structure_timers?map_id=<uuid>&system_id=31002229
|
||||
```
|
||||
"""
|
||||
@spec show_structure_timers(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :show_structure_timers,
|
||||
summary: "Show Structure Timers",
|
||||
description: "Retrieves structure timers for a map. Requires either 'map_id' or 'slug' as a query parameter to identify the map.",
|
||||
parameters: [
|
||||
map_id: [
|
||||
in: :query,
|
||||
description: "Map identifier (UUID) - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: ""
|
||||
],
|
||||
slug: [
|
||||
in: :query,
|
||||
description: "Map slug - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "map-name"
|
||||
],
|
||||
system_id: [
|
||||
in: :query,
|
||||
description: "System ID",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "30000142"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {
|
||||
"Structure timers",
|
||||
"application/json",
|
||||
@structure_timers_response_schema
|
||||
},
|
||||
bad_request: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Must provide either ?map_id=UUID or ?slug=SLUG as a query parameter"
|
||||
}
|
||||
}}
|
||||
]
|
||||
def show_structure_timers(conn, params) do
|
||||
with {:ok, map_id} <- Util.fetch_map_id(params) do
|
||||
system_id_str = params["system_id"]
|
||||
@@ -191,6 +525,50 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
GET /api/map/systems_kills?slug=<map-slug>
|
||||
GET /api/map/systems_kills?map_id=<uuid>&hours_ago=<somehours>
|
||||
"""
|
||||
@spec list_systems_kills(Plug.Conn.t(), map()) :: Plug.Conn.t()
|
||||
operation :list_systems_kills,
|
||||
summary: "List Systems Kills",
|
||||
description: "Returns kills data for all visible systems on the map, optionally filtered by hours_ago. Requires either 'map_id' or 'slug' as a query parameter to identify the map.",
|
||||
parameters: [
|
||||
map_id: [
|
||||
in: :query,
|
||||
description: "Map identifier (UUID) - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: ""
|
||||
],
|
||||
slug: [
|
||||
in: :query,
|
||||
description: "Map slug - Either map_id or slug must be provided",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "map-name"
|
||||
],
|
||||
hours: [
|
||||
in: :query,
|
||||
description: "Number of hours to look back for kills",
|
||||
type: :string,
|
||||
required: false,
|
||||
example: "24"
|
||||
]
|
||||
],
|
||||
responses: [
|
||||
ok: {
|
||||
"Systems kills data",
|
||||
"application/json",
|
||||
@systems_kills_response_schema
|
||||
},
|
||||
bad_request: {"Error", "application/json", %OpenApiSpex.Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
error: %OpenApiSpex.Schema{type: :string}
|
||||
},
|
||||
required: ["error"],
|
||||
example: %{
|
||||
"error" => "Must provide either ?map_id=UUID or ?slug=SLUG as a query parameter"
|
||||
}
|
||||
}}
|
||||
]
|
||||
def list_systems_kills(conn, params) do
|
||||
with {:ok, map_id} <- Util.fetch_map_id(params),
|
||||
# fetch visible systems from the repo
|
||||
@@ -245,15 +623,6 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
GET /api/map/systems-kills
|
||||
|
||||
This is an alias for list_systems_kills to support the hyphenated URL format.
|
||||
See list_systems_kills for full documentation.
|
||||
"""
|
||||
def list_systems_kills_hyphenated(conn, params) do
|
||||
list_systems_kills(conn, params)
|
||||
end
|
||||
|
||||
# If hours_str is present and valid, parse it. Otherwise return nil (no filter).
|
||||
defp parse_hours_ago(nil), do: nil
|
||||
@@ -409,11 +778,14 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
end
|
||||
|
||||
defp map_system_to_json(system) do
|
||||
Map.take(system, [
|
||||
# Get the original system name from the database
|
||||
original_name = get_original_system_name(system.solar_system_id)
|
||||
|
||||
# Start with the basic system data
|
||||
result = Map.take(system, [
|
||||
:id,
|
||||
:map_id,
|
||||
:solar_system_id,
|
||||
:name,
|
||||
:custom_name,
|
||||
:temporary_name,
|
||||
:description,
|
||||
@@ -427,6 +799,35 @@ defmodule WandererAppWeb.MapAPIController do
|
||||
:inserted_at,
|
||||
:updated_at
|
||||
])
|
||||
|
||||
# Add the original name
|
||||
result = Map.put(result, :original_name, original_name)
|
||||
|
||||
# Set the name field based on the display priority:
|
||||
# 1. If temporary_name is set, use that
|
||||
# 2. If custom_name is set, use that
|
||||
# 3. Otherwise, use the original system name
|
||||
display_name = cond do
|
||||
not is_nil(system.temporary_name) and system.temporary_name != "" ->
|
||||
system.temporary_name
|
||||
not is_nil(system.custom_name) and system.custom_name != "" ->
|
||||
system.custom_name
|
||||
true ->
|
||||
original_name
|
||||
end
|
||||
|
||||
# Add the display name as the "name" field
|
||||
Map.put(result, :name, display_name)
|
||||
end
|
||||
|
||||
defp get_original_system_name(solar_system_id) do
|
||||
# Fetch the original system name from the MapSolarSystem resource
|
||||
case WandererApp.Api.MapSolarSystem.by_solar_system_id(solar_system_id) do
|
||||
{:ok, system} ->
|
||||
system.solar_system_name
|
||||
_error ->
|
||||
"Unknown System"
|
||||
end
|
||||
end
|
||||
|
||||
defp character_to_json(ch) do
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
defmodule WandererAppWeb.Plugs.CheckMapApiKey do
|
||||
@moduledoc """
|
||||
A plug that checks the "Authorization: Bearer <token>" header
|
||||
against the map’s stored public_api_key. Halts with 401 if invalid.
|
||||
against the map's stored public_api_key. Halts with 401 if invalid.
|
||||
"""
|
||||
|
||||
import Plug.Conn
|
||||
@@ -20,19 +20,22 @@ defmodule WandererAppWeb.Plugs.CheckMapApiKey do
|
||||
conn
|
||||
else
|
||||
conn
|
||||
|> send_resp(401, "Unauthorized (invalid token for map)")
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(401, Jason.encode!(%{error: "Unauthorized (invalid token for map)"}))
|
||||
|> halt()
|
||||
end
|
||||
|
||||
{:error, _reason} ->
|
||||
conn
|
||||
|> send_resp(404, "Map not found")
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(404, Jason.encode!(%{error: "Map not found"}))
|
||||
|> halt()
|
||||
end
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> send_resp(401, "Missing or invalid 'Bearer' token")
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(401, Jason.encode!(%{error: "Missing or invalid 'Bearer' token"}))
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18,23 +18,78 @@ defmodule WandererAppWeb.Router do
|
||||
[WandererAppWeb.Endpoint, :code_reloader],
|
||||
false
|
||||
)
|
||||
@frame_src if(@code_reloading, do: ~w('self'), else: ~w())
|
||||
@style_src ~w('self' 'unsafe-inline' https://fonts.googleapis.com)
|
||||
@img_src ~w('self' data: https://images.evetech.net https://web.ccpgamescdn.com https://images.ctfassets.net https://w.appzi.io)
|
||||
@font_src ~w('self' https://fonts.gstatic.com data: https://web.ccpgamescdn.com https://w.appzi.io )
|
||||
@script_src ~w('self' )
|
||||
@frame_src_values if(@code_reloading, do: ["'self'"], else: [])
|
||||
|
||||
# Define style sources individually to ensure proper spacing
|
||||
@style_src_values [
|
||||
"'self'",
|
||||
"'unsafe-inline'",
|
||||
"https://fonts.googleapis.com",
|
||||
"https://cdn.jsdelivr.net/npm/",
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/"
|
||||
]
|
||||
|
||||
# Define image sources individually to ensure proper spacing
|
||||
@img_src_values [
|
||||
"'self'",
|
||||
"data:",
|
||||
"https://images.evetech.net",
|
||||
"https://web.ccpgamescdn.com",
|
||||
"https://images.ctfassets.net",
|
||||
"https://w.appzi.io"
|
||||
]
|
||||
|
||||
# Define font sources individually to ensure proper spacing
|
||||
@font_src_values [
|
||||
"'self'",
|
||||
"https://fonts.gstatic.com",
|
||||
"data:",
|
||||
"https://web.ccpgamescdn.com",
|
||||
"https://w.appzi.io"
|
||||
]
|
||||
|
||||
# Define script sources individually to ensure proper spacing
|
||||
@script_src_values [
|
||||
"'self'",
|
||||
"'unsafe-inline'",
|
||||
"https://cdn.jsdelivr.net/npm/",
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/",
|
||||
"https://unpkg.com",
|
||||
"https://cdn.jsdelivr.net",
|
||||
"https://w.appzi.io",
|
||||
"https://www.googletagmanager.com",
|
||||
"https://cdnjs.cloudflare.com"
|
||||
]
|
||||
|
||||
# Define connect sources individually to ensure proper spacing
|
||||
@connect_src_values [
|
||||
"'self'",
|
||||
"https://api.appzi.io",
|
||||
"https://www.googletagmanager.com",
|
||||
"https://www.google-analytics.com"
|
||||
]
|
||||
|
||||
# Define sandbox values individually to ensure proper spacing
|
||||
@sandbox_values [
|
||||
"allow-forms",
|
||||
"allow-scripts",
|
||||
"allow-modals",
|
||||
"allow-same-origin",
|
||||
"allow-downloads",
|
||||
"allow-popups"
|
||||
]
|
||||
|
||||
pipeline :admin_bauth do
|
||||
plug :admin_basic_auth
|
||||
end
|
||||
|
||||
pipeline :browser do
|
||||
plug(:accepts, ["html"])
|
||||
plug(:fetch_session)
|
||||
plug(:fetch_live_flash)
|
||||
plug(:put_root_layout, html: {WandererAppWeb.Layouts, :root})
|
||||
plug(:protect_from_forgery)
|
||||
plug(:put_secure_browser_headers)
|
||||
plug :accepts, ["html"]
|
||||
plug :fetch_session
|
||||
plug :fetch_live_flash
|
||||
plug :put_root_layout, html: {WandererAppWeb.Layouts, :root}
|
||||
plug :protect_from_forgery
|
||||
plug :put_secure_browser_headers
|
||||
|
||||
dynamic_plug PlugContentSecurityPolicy, reevaluate: :first_usage do
|
||||
URI.default_port("wss", 443)
|
||||
@@ -51,41 +106,37 @@ defmodule WandererAppWeb.Router do
|
||||
|> Map.put(:path, "")
|
||||
|> URI.to_string()
|
||||
|
||||
# Get the HTTP URL from home_url
|
||||
http_url = URI.to_string(home_url)
|
||||
|
||||
# Only add script-src-elem when in development mode
|
||||
script_src_elem = if(@code_reloading, do:
|
||||
@script_src_values ++ [ws_url, http_url],
|
||||
else: @script_src_values)
|
||||
|
||||
directives = %{
|
||||
default_src: ~w('none'),
|
||||
script_src: [
|
||||
@script_src,
|
||||
~w('unsafe-inline'),
|
||||
~w(https://unpkg.com),
|
||||
~w(https://cdn.jsdelivr.net),
|
||||
~w(https://w.appzi.io),
|
||||
~w(https://www.googletagmanager.com),
|
||||
~w(https://cdnjs.cloudflare.com)
|
||||
],
|
||||
style_src: @style_src,
|
||||
img_src: @img_src,
|
||||
font_src: @font_src,
|
||||
connect_src: [
|
||||
ws_url,
|
||||
~w('self'),
|
||||
~w(https://api.appzi.io),
|
||||
~w(https://www.googletagmanager.com),
|
||||
~w(https://www.google-analytics.com)
|
||||
],
|
||||
script_src: @script_src_values ++ [ws_url],
|
||||
style_src: @style_src_values,
|
||||
img_src: @img_src_values,
|
||||
font_src: @font_src_values,
|
||||
connect_src: @connect_src_values ++ [ws_url],
|
||||
media_src: ~w('none'),
|
||||
object_src: ~w('none'),
|
||||
child_src: ~w('none'),
|
||||
frame_src: [@frame_src],
|
||||
frame_src: @frame_src_values,
|
||||
worker_src: ~w('none'),
|
||||
frame_ancestors: ~w('none'),
|
||||
form_action: ~w('self'),
|
||||
block_all_mixed_content: ~w(),
|
||||
sandbox:
|
||||
~w(allow-forms allow-scripts allow-modals allow-same-origin allow-downloads allow-popups),
|
||||
sandbox: @sandbox_values,
|
||||
base_uri: ~w('none'),
|
||||
manifest_src: ~w('self')
|
||||
}
|
||||
|
||||
# Only add script-src-elem to directives when in development mode
|
||||
directives = Map.put(directives, :script_src_elem, script_src_elem)
|
||||
|
||||
directives =
|
||||
case home_url do
|
||||
%URI{scheme: "http"} -> directives
|
||||
@@ -101,11 +152,11 @@ defmodule WandererAppWeb.Router do
|
||||
end
|
||||
|
||||
pipeline :blog do
|
||||
plug(:put_layout, html: {WandererAppWeb.Layouts, :blog})
|
||||
plug :put_layout, html: {WandererAppWeb.Layouts, :blog}
|
||||
end
|
||||
|
||||
pipeline :api do
|
||||
plug(:accepts, ["json"])
|
||||
plug :accepts, ["json"]
|
||||
plug WandererAppWeb.Plugs.CheckApiDisabled
|
||||
end
|
||||
|
||||
@@ -126,6 +177,12 @@ defmodule WandererAppWeb.Router do
|
||||
plug WandererAppWeb.Plugs.CheckAclApiKey
|
||||
end
|
||||
|
||||
pipeline :api_spec do
|
||||
plug OpenApiSpex.Plug.PutApiSpec,
|
||||
otp_app: :wanderer_app,
|
||||
module: WandererAppWeb.ApiSpec
|
||||
end
|
||||
|
||||
scope "/api/map/systems-kills", WandererAppWeb do
|
||||
pipe_through [:api, :api_map, :api_kills]
|
||||
|
||||
@@ -162,6 +219,11 @@ defmodule WandererAppWeb.Router do
|
||||
get "/system-static-info", CommonAPIController, :show_system_static
|
||||
end
|
||||
|
||||
scope "/api" do
|
||||
pipe_through [:browser, :api, :api_spec]
|
||||
get "/openapi", OpenApiSpex.Plug.RenderSpec, :show
|
||||
end
|
||||
|
||||
#
|
||||
# Browser / blog stuff
|
||||
#
|
||||
@@ -191,6 +253,30 @@ defmodule WandererAppWeb.Router do
|
||||
get "/", BlogController, :license
|
||||
end
|
||||
|
||||
scope "/swaggerui" do
|
||||
pipe_through [:browser, :api, :api_spec]
|
||||
|
||||
get "/", OpenApiSpex.Plug.SwaggerUI,
|
||||
path: "/api/openapi",
|
||||
title: "WandererApp API Docs",
|
||||
css_urls: [
|
||||
# Standard Swagger UI CSS
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.5.0/swagger-ui.min.css",
|
||||
# Material theme from swagger-ui-themes (v3.x):
|
||||
"https://cdn.jsdelivr.net/npm/swagger-ui-themes@3.0.0/themes/3.x/theme-material.css"
|
||||
],
|
||||
js_urls: [
|
||||
# We need both main JS & standalone preset for full styling
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.5.0/swagger-ui-bundle.min.js",
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/4.5.0/swagger-ui-standalone-preset.min.js"
|
||||
],
|
||||
favicon_url: "https://example.com/my_favicon.ico",
|
||||
swagger_ui_config: %{
|
||||
"docExpansion" => "none",
|
||||
"deepLinking" => true
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Auth
|
||||
#
|
||||
|
||||
1
mix.exs
1
mix.exs
@@ -54,6 +54,7 @@ defmodule WandererApp.MixProject do
|
||||
{:sobelow, ">= 0.0.0", only: [:dev], runtime: false},
|
||||
{:mix_audit, ">= 0.0.0", only: [:dev], runtime: false},
|
||||
{:ex_check, "~> 0.14.0", only: [:dev], runtime: false},
|
||||
{:open_api_spex, github: "mbuhot/open_api_spex", branch: "master"},
|
||||
{:ex_rated, "~> 2.0"},
|
||||
{:retry, "~> 0.18.0"},
|
||||
{:phoenix, "~> 1.7.12"},
|
||||
|
||||
2
mix.lock
2
mix.lock
@@ -78,6 +78,7 @@
|
||||
"nimble_publisher": {:hex, :nimble_publisher, "1.1.0", "49dee0f30536140268996660a5927d0282946949c35c88ccc6da11a19231b4b6", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "80fb42d8d1e34f41ff29fc2a1ae6ab86ea7b764b3c2d38e5268a43cf33825782"},
|
||||
"oauth2": {:hex, :oauth2, "2.1.0", "beb657f393814a3a7a8a15bd5e5776ecae341fd344df425342a3b6f1904c2989", [:mix], [{:tesla, "~> 1.5", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "8ac07f85b3307dd1acfeb0ec852f64161b22f57d0ce0c15e616a1dfc8ebe2b41"},
|
||||
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
|
||||
"open_api_spex": {:git, "https://github.com/mbuhot/open_api_spex.git", "abe90e3db0cab2e75ede364ee24f26c9e490f74f", [branch: "master"]},
|
||||
"owl": {:hex, :owl, "0.11.0", "2cd46185d330aa2400f1c8c3cddf8d2ff6320baeff23321d1810e58127082cae", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "73f5783f0e963cc04a061be717a0dbb3e49ae0c4bfd55fb4b78ece8d33a65efe"},
|
||||
"parent": {:hex, :parent, "0.12.1", "495c4386f06de0df492e0a7a7199c10323a55e9e933b27222060dd86dccd6d62", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2ab589ef1f37bfcedbfb5ecfbab93354972fb7391201b8907a866dadd20b39d1"},
|
||||
"pathex": {:hex, :pathex, "2.5.3", "0f2674c7cb52ae9220766cae2653b4013578349ae5ec07cb0c31b92684b3f19a", [:mix], [], "hexpm", "767aefc27d0303f583ba2064f0a49546067ab5de3c42b89f014a0ba32ea04830"},
|
||||
@@ -103,6 +104,7 @@
|
||||
"quantum": {:hex, :quantum, "3.5.3", "ee38838a07761663468145f489ad93e16a79440bebd7c0f90dc1ec9850776d99", [:mix], [{:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.14 or ~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_registry, "~> 0.2", [hex: :telemetry_registry, repo: "hexpm", optional: false]}], "hexpm", "500fd3fa77dcd723ed9f766d4a175b684919ff7b6b8cfd9d7d0564d58eba8734"},
|
||||
"ranch": {:hex, :ranch, "2.2.0", "25528f82bc8d7c6152c57666ca99ec716510fe0925cb188172f41ce93117b1b0", [:make, :rebar3], [], "hexpm", "fa0b99a1780c80218a4197a59ea8d3bdae32fbff7e88527d7d8a4787eff4f8e7"},
|
||||
"reactor": {:hex, :reactor, "0.10.0", "1206113c21ba69b889e072b2c189c05a7aced523b9c3cb8dbe2dab7062cb699a", [:mix], [{:igniter, "~> 0.2", [hex: :igniter, repo: "hexpm", optional: false]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4003c33e4c8b10b38897badea395e404d74d59a31beb30469a220f2b1ffe6457"},
|
||||
"redoc_ui_plug": {:hex, :redoc_ui_plug, "0.2.1", "5e9760c17ed450fc9df671d5fbc70a6f06179c41d9d04ae3c33f16baca3a5b19", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "7be01db31f210887e9fc18f8fbccc7788de32c482b204623556e415ed1fe714b"},
|
||||
"req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"},
|
||||
"retry": {:hex, :retry, "0.18.0", "dc58ebe22c95aa00bc2459f9e0c5400e6005541cf8539925af0aa027dc860543", [:mix], [], "hexpm", "9483959cc7bf69c9e576d9dfb2b678b71c045d3e6f39ab7c9aa1489df4492d73"},
|
||||
"rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "51cc347a4269ad3a1e7a2c4122dbac9198302b082f5615964358b4635ebf3d4f"},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
Wanderer’s expanded public API now lets you retrieve **all characters** in the system and manage “Access Lists” (ACLs) for controlling visibility or permissions. These endpoints allow you to:
|
||||
Wanderer's expanded public API now lets you retrieve **all characters** in the system and manage "Access Lists" (ACLs) for controlling visibility or permissions. These endpoints allow you to:
|
||||
|
||||
- Fetch a list of **all** EVE characters known to the system.
|
||||
- List ACLs for a given map.
|
||||
@@ -30,8 +30,8 @@ Unless otherwise noted, these endpoints require a valid **Bearer** token. Pass i
|
||||
Authorization: Bearer <REDACTED_TOKEN>
|
||||
```
|
||||
|
||||
If the token is missing or invalid, you’ll receive a `401 Unauthorized` error.
|
||||
_(No API key is required for some “common” endpoints, but ACL- and character-related endpoints require a valid token.)_
|
||||
If the token is missing or invalid, you'll receive a `401 Unauthorized` error.
|
||||
_(No API key is required for some "common" endpoints, but ACL- and character-related endpoints require a valid token.)_
|
||||
|
||||
There are two types of tokens in use:
|
||||
|
||||
@@ -152,17 +152,35 @@ curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
||||
"members": [
|
||||
{
|
||||
"id": "8d63ab1e-b44f-4e81-8227-8fb8d928dad8",
|
||||
"name": "Other Character",
|
||||
"name": "Character Name",
|
||||
"role": "admin",
|
||||
"eve_character_id": "2122019111",
|
||||
"inserted_at": "2025-02-13T03:33:32.332598Z",
|
||||
"updated_at": "2025-02-13T03:33:36.644520Z"
|
||||
},
|
||||
...
|
||||
{
|
||||
"id": "7e52ab1e-c33f-5e81-9338-7fb8d928ebc9",
|
||||
"name": "Corporation Name",
|
||||
"role": "viewer",
|
||||
"eve_corporation_id": "98140648",
|
||||
"inserted_at": "2025-02-13T03:33:32.332598Z",
|
||||
"updated_at": "2025-02-13T03:33:36.644520Z"
|
||||
},
|
||||
{
|
||||
"id": "6f41bc2f-d44e-6f92-8449-8ec9e039fad7",
|
||||
"name": "Alliance Name",
|
||||
"role": "viewer",
|
||||
"eve_alliance_id": "99013806",
|
||||
"inserted_at": "2025-02-13T03:33:32.332598Z",
|
||||
"updated_at": "2025-02-13T03:33:36.644520Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The response for each member will include only one of `eve_character_id`, `eve_corporation_id`, or `eve_alliance_id` depending on the type of member.
|
||||
|
||||
---
|
||||
|
||||
### 4. Create a New ACL Associated with a Map
|
||||
@@ -295,14 +313,13 @@ POST /api/acls/:acl_id/members
|
||||
```json
|
||||
{
|
||||
"member": {
|
||||
"name": "New Member",
|
||||
"eve_character_id": "EXTERNAL_EVE_ID",
|
||||
"role": "viewer"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Example Request:**
|
||||
- **Example Request for Character:**
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
@@ -310,7 +327,6 @@ curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"member": {
|
||||
"name": "New Member",
|
||||
"eve_character_id": "EXTERNAL_EVE_ID",
|
||||
"role": "viewer"
|
||||
}
|
||||
@@ -318,14 +334,45 @@ curl -X POST \
|
||||
"https://wanderer.example.com/api/acls/ACL_UUID/members"
|
||||
```
|
||||
|
||||
- **Example Response (redacted):**
|
||||
- **Example Request for Corporation:**
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer <ACL_API_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"member": {
|
||||
"eve_corporation_id": "CORPORATION_ID",
|
||||
"role": "viewer"
|
||||
}
|
||||
}' \
|
||||
"https://wanderer.example.com/api/acls/ACL_UUID/members"
|
||||
```
|
||||
|
||||
- **Example Response for Character (redacted):**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "MEMBERSHIP_UUID",
|
||||
"name": "New Member",
|
||||
"name": "Character Name",
|
||||
"role": "viewer",
|
||||
"eve_character_id": "EXTERNAL_EVE_ID",
|
||||
"inserted_at": "...",
|
||||
"updated_at": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- **Example Response for Corporation (redacted):**
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "MEMBERSHIP_UUID",
|
||||
"name": "Corporation Name",
|
||||
"role": "viewer",
|
||||
"eve_corporation_id": "CORPORATION_ID",
|
||||
"inserted_at": "...",
|
||||
"updated_at": "..."
|
||||
}
|
||||
@@ -334,13 +381,13 @@ curl -X POST \
|
||||
|
||||
---
|
||||
|
||||
### 7. Change a Member’s Role
|
||||
### 7. Change a Member's Role
|
||||
|
||||
```bash
|
||||
PUT /api/acls/:acl_id/members/:member_id
|
||||
```
|
||||
|
||||
- **Description:** Updates an ACL member’s role (e.g. from `viewer` to `admin`).
|
||||
- **Description:** Updates an ACL member's role (e.g. from `viewer` to `admin`).
|
||||
The `:member_id` is the external EVE id (or corp/alliance id) used when creating the membership.
|
||||
- **Authentication:** Requires the ACL API Token.
|
||||
- **Request Body Example:**
|
||||
@@ -373,13 +420,17 @@ curl -X PUT \
|
||||
{
|
||||
"data": {
|
||||
"id": "MEMBERSHIP_UUID",
|
||||
"name": "New Member",
|
||||
"name": "Character Name",
|
||||
"role": "admin",
|
||||
...
|
||||
"eve_character_id": "EXTERNAL_EVE_ID",
|
||||
"inserted_at": "...",
|
||||
"updated_at": "..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The response will include only one of `eve_character_id`, `eve_corporation_id`, or `eve_alliance_id` depending on the type of member.
|
||||
|
||||
---
|
||||
|
||||
### 8. Remove a Member from an ACL
|
||||
@@ -416,7 +467,7 @@ This guide outlines how to:
|
||||
4. **Create** a new ACL for a map (`POST /api/map/acls`), which generates a new ACL API key.
|
||||
5. **Update** an existing ACL (`PUT /api/acls/:id`).
|
||||
6. **Add** members (characters, corporations, alliances) to an ACL (`POST /api/acls/:acl_id/members`).
|
||||
7. **Change** a member’s role (`PUT /api/acls/:acl_id/members/:member_id`).
|
||||
7. **Change** a member's role (`PUT /api/acls/:acl_id/members/:member_id`).
|
||||
8. **Remove** a member from an ACL (`DELETE /api/acls/:acl_id/members/:member_id`).
|
||||
|
||||
By following these request patterns, you can manage your ACL resources in a fully programmatic fashion. If you have any questions, feel free to reach out to the Wanderer Team.
|
||||
|
||||
837
priv/posts/2025/03-05-api.md
Normal file
837
priv/posts/2025/03-05-api.md
Normal file
@@ -0,0 +1,837 @@
|
||||
%{
|
||||
title: "Comprehensive Guide: Wanderer API Documentation",
|
||||
author: "Wanderer Team",
|
||||
cover_image_uri: "/images/news/03-05-api/swagger-ui.png",
|
||||
tags: ~w(api map acl characters documentation swagger),
|
||||
description: "Complete documentation for Wanderer's public APIs, including map data, character information, and access control management. Includes interactive API documentation with Swagger UI."
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
# Comprehensive Guide to Wanderer's API
|
||||
|
||||
## Introduction
|
||||
|
||||
Wanderer provides a comprehensive set of public APIs that allow you to programmatically interact with the platform. This guide consolidates all available API endpoints, authentication methods, and includes interactive documentation options.
|
||||
|
||||
With these APIs, you can:
|
||||
|
||||
- Retrieve map data, including systems and their properties
|
||||
- Access system static information
|
||||
- Track character locations and activities
|
||||
- Monitor kill activity in systems
|
||||
- Manage Access Control Lists (ACLs) for permissions
|
||||
- Add, update, and remove ACL members
|
||||
|
||||
This guide provides step-by-step instructions, request/response examples, and details on how to authenticate each call.
|
||||
|
||||
---
|
||||
|
||||
## Interactive API Documentation
|
||||
|
||||
For a more interactive experience, Wanderer provides a way to explore the API:
|
||||
|
||||
### Swagger UI
|
||||
|
||||
Access our Swagger UI documentation at:
|
||||
|
||||
```
|
||||
/swaggerui
|
||||
```
|
||||
|
||||
This interactive interface allows you to:
|
||||
- Browse all available endpoints
|
||||
- See request parameters and response schemas
|
||||
- Test API calls directly from your browser
|
||||
- View authentication requirements
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
Wanderer uses Bearer token authentication for API access. There are two types of tokens in use:
|
||||
|
||||
1. **Map API Token:** Available in the map settings. This token is used for map-specific endpoints.
|
||||
|
||||

|
||||
|
||||
2. **ACL API Token:** Available in the create/edit ACL screen. This token is used for ACL member management endpoints.
|
||||
|
||||

|
||||
|
||||
Pass the appropriate token in the `Authorization` header:
|
||||
|
||||
```bash
|
||||
Authorization: Bearer <YOUR_TOKEN>
|
||||
```
|
||||
|
||||
If the token is missing or invalid, you'll receive a `401 Unauthorized` error.
|
||||
|
||||
**Note:** Some "common" endpoints (like system static information) don't require authentication.
|
||||
|
||||
---
|
||||
|
||||
## Map Data Endpoints
|
||||
|
||||
### 1. List Systems
|
||||
|
||||
```bash
|
||||
GET /api/map/systems?map_id=<UUID>
|
||||
GET /api/map/systems?slug=<map-slug>
|
||||
```
|
||||
|
||||
- **Description:** Retrieves a list of systems associated with the specified map.
|
||||
- **Authentication:** Requires Map API Token.
|
||||
- **Parameters:**
|
||||
- `map_id` (optional if `slug` is provided) — the UUID of the map.
|
||||
- `slug` (optional if `map_id` is provided) — the slug identifier of the map.
|
||||
- `all=true` (optional) — if set, returns _all_ systems instead of only "visible" systems.
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
||||
"https://wanderer.example.com/api/map/systems?slug=some-slug"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "<REDACTED_ID>",
|
||||
"name": "<REDACTED_NAME>",
|
||||
"status": 0,
|
||||
"tag": null,
|
||||
"visible": false,
|
||||
"description": null,
|
||||
"labels": "<REDACTED_JSON>",
|
||||
"inserted_at": "2025-01-01T13:38:42.875843Z",
|
||||
"updated_at": "2025-01-01T13:40:16.750234Z",
|
||||
"locked": false,
|
||||
"solar_system_id": "<REDACTED_NUMBER>",
|
||||
"map_id": "<REDACTED_ID>",
|
||||
"custom_name": null,
|
||||
"position_x": 1125,
|
||||
"position_y": -285
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Show Single System
|
||||
|
||||
```bash
|
||||
GET /api/map/system?id=<SOLAR_SYSTEM_ID>&map_id=<UUID>
|
||||
GET /api/map/system?id=<SOLAR_SYSTEM_ID>&slug=<map-slug>
|
||||
```
|
||||
|
||||
- **Description:** Retrieves information for a specific system on the specified map.
|
||||
- **Authentication:** Requires Map API Token.
|
||||
- **Parameters:**
|
||||
- `id` (required) — the `solar_system_id`.
|
||||
- Either `map_id` or `slug` (required).
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
||||
"https://wanderer.example.com/api/map/system?id=<REDACTED_NUMBER>&slug=<REDACTED_SLUG>"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "<REDACTED_ID>",
|
||||
"name": "<REDACTED_NAME>",
|
||||
"status": 0,
|
||||
"tag": null,
|
||||
"visible": false,
|
||||
"description": null,
|
||||
"labels": "<REDACTED_JSON>",
|
||||
"inserted_at": "2025-01-03T06:30:02.069090Z",
|
||||
"updated_at": "2025-01-03T07:47:07.471051Z",
|
||||
"locked": false,
|
||||
"solar_system_id": "<REDACTED_NUMBER>",
|
||||
"map_id": "<REDACTED_ID>",
|
||||
"custom_name": null,
|
||||
"position_x": 1005,
|
||||
"position_y": 765
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. System Static Information
|
||||
|
||||
```bash
|
||||
GET /api/common/system-static-info?id=<SOLAR_SYSTEM_ID>
|
||||
```
|
||||
|
||||
- **Description:** Retrieves the static information for a specific system.
|
||||
- **Authentication:** No authentication required.
|
||||
- **Parameters:**
|
||||
- `id` (required) — the `solar_system_id`.
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl "https://wanderer.example.com/api/common/system-static-info?id=31002229"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"solar_system_id": 31002229,
|
||||
"triglavian_invasion_status": "Normal",
|
||||
"solar_system_name": "J132946",
|
||||
"system_class": 5,
|
||||
"region_id": 11000028,
|
||||
"constellation_id": 21000278,
|
||||
"solar_system_name_lc": "j132946",
|
||||
"constellation_name": "E-C00278",
|
||||
"region_name": "E-R00028",
|
||||
"security": "-1.0",
|
||||
"type_description": "Class 5",
|
||||
"class_title": "C5",
|
||||
"is_shattered": false,
|
||||
"effect_name": null,
|
||||
"effect_power": 5,
|
||||
"statics": [
|
||||
"H296"
|
||||
],
|
||||
"wandering": [
|
||||
"D792",
|
||||
"C140",
|
||||
"Z142"
|
||||
],
|
||||
"sun_type_id": 38
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. List Tracked Characters
|
||||
|
||||
```bash
|
||||
GET /api/map/characters?map_id=<UUID>
|
||||
GET /api/map/characters?slug=<map-slug>
|
||||
```
|
||||
|
||||
- **Description:** Retrieves a list of tracked characters for the specified map.
|
||||
- **Authentication:** Requires Map API Token.
|
||||
- **Parameters:**
|
||||
- `map_id` (optional if `slug` is provided) — the UUID of the map.
|
||||
- `slug` (optional if `map_id` is provided) — the slug identifier of the map.
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
||||
"https://wanderer.example.com/api/map/characters?slug=some-slug"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "<REDACTED_ID>",
|
||||
"character": {
|
||||
"id": "<REDACTED_ID>",
|
||||
"name": "<REDACTED_NAME>",
|
||||
"inserted_at": "2025-01-01T05:24:18.461721Z",
|
||||
"updated_at": "2025-01-03T07:45:52.294052Z",
|
||||
"alliance_id": "<REDACTED>",
|
||||
"alliance_name": "<REDACTED>",
|
||||
"alliance_ticker": "<REDACTED>",
|
||||
"corporation_id": "<REDACTED>",
|
||||
"corporation_name": "<REDACTED>",
|
||||
"corporation_ticker": "<REDACTED>",
|
||||
"eve_id": "<REDACTED>"
|
||||
},
|
||||
"tracked": true,
|
||||
"map_id": "<REDACTED_ID>"
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Kills Activity
|
||||
|
||||
```bash
|
||||
GET /api/map/systems-kills?map_id=<UUID>
|
||||
GET /api/map/systems-kills?slug=<map-slug>
|
||||
```
|
||||
|
||||
- **Description:** Retrieves the kill activity for the specified map.
|
||||
- **Authentication:** Requires Map API Token.
|
||||
- **Parameters:**
|
||||
- `map_id` (optional if `slug` is provided) — the UUID of the map.
|
||||
- `slug` (optional if `map_id` is provided) — the slug identifier of the map.
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
||||
"https://wanderer.example.com/api/map/systems-kills?slug=some-slug"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"kills": [
|
||||
{
|
||||
"attacker_count": 1,
|
||||
"final_blow_alliance_id": 99013806,
|
||||
"final_blow_alliance_ticker": "TCE",
|
||||
"final_blow_char_id": 2116802670,
|
||||
"final_blow_char_name": "Bambi Bunny",
|
||||
"final_blow_corp_id": 98140648,
|
||||
"final_blow_corp_ticker": "GNK3D",
|
||||
"final_blow_ship_name": "Thrasher",
|
||||
"final_blow_ship_type_id": 16242,
|
||||
"kill_time": "2025-01-21T21:00:59Z",
|
||||
"killmail_id": 124181782,
|
||||
"npc": false,
|
||||
"solar_system_id": 30002768,
|
||||
"total_value": 10000,
|
||||
"victim_alliance_id": null,
|
||||
"victim_char_id": 2121725410,
|
||||
"victim_char_name": "Bill Drummond",
|
||||
"victim_corp_id": 98753095,
|
||||
"victim_corp_ticker": "KSTJK",
|
||||
"victim_ship_name": "Capsule",
|
||||
"victim_ship_type_id": 670,
|
||||
"zkb": {
|
||||
"awox": false,
|
||||
"destroyedValue": 10000,
|
||||
"droppedValue": 0,
|
||||
"fittedValue": 10000,
|
||||
"hash": "777148f8bf344bade68a6a0821bfe0a37491a7a6",
|
||||
"labels": ["cat:6","#:1","pvp","loc:highsec"],
|
||||
"locationID": 50014064,
|
||||
"npc": false,
|
||||
"points": 1,
|
||||
"solo": false,
|
||||
"totalValue": 10000
|
||||
}
|
||||
},
|
||||
...
|
||||
],
|
||||
"solar_system_id": 30002768
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Structure Timers
|
||||
|
||||
```bash
|
||||
GET /api/map/structure-timers?map_id=<UUID>
|
||||
GET /api/map/structure-timers?slug=<map-slug>
|
||||
```
|
||||
|
||||
- **Description:** Retrieves structure timers for the specified map.
|
||||
- **Authentication:** Requires Map API Token.
|
||||
- **Parameters:**
|
||||
- `map_id` (optional if `slug` is provided) — the UUID of the map.
|
||||
- `slug` (optional if `map_id` is provided) — the slug identifier of the map.
|
||||
|
||||
---
|
||||
|
||||
## Character and ACL Endpoints
|
||||
|
||||
### 1. List All Characters
|
||||
|
||||
```bash
|
||||
GET /api/characters
|
||||
```
|
||||
|
||||
- **Description:** Returns a list of all characters known to Wanderer.
|
||||
- **Authentication:** Requires a valid API token.
|
||||
- **Toggle:** Controlled by the environment variable `WANDERER_CHARACTER_API_DISABLED` (default is `false`).
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
||||
"https://wanderer.example.com/api/characters"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "b374d9e6-47a7-4e20-85ad-d608809827b5",
|
||||
"name": "Some Character",
|
||||
"eve_id": "2122825111",
|
||||
"corporation_name": "School of Applied Knowledge",
|
||||
"alliance_name": null
|
||||
},
|
||||
{
|
||||
"id": "6963bee6-eaa1-40e2-8200-4bc2fcbd7350",
|
||||
"name": "Other Character",
|
||||
"eve_id": "2122019111",
|
||||
"corporation_name": "Some Corporation",
|
||||
"alliance_name": null
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Use the `eve_id` when referencing a character in ACL operations.
|
||||
|
||||
### 2. List ACLs for a Map
|
||||
|
||||
```bash
|
||||
GET /api/map/acls?map_id=<UUID>
|
||||
GET /api/map/acls?slug=<map-slug>
|
||||
```
|
||||
|
||||
- **Description:** Lists all ACLs associated with a map.
|
||||
- **Authentication:** Requires Map API Token.
|
||||
- **Parameters:**
|
||||
- `map_id` (optional if `slug` is provided) — the UUID of the map.
|
||||
- `slug` (optional if `map_id` is provided) — the slug identifier of the map.
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
||||
"https://wanderer.example.com/api/map/acls?slug=mapname"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "19712899-ec3a-47b1-b73b-2bae221c5513",
|
||||
"name": "aclName",
|
||||
"description": null,
|
||||
"owner_eve_id": "11111111111",
|
||||
"inserted_at": "2025-02-13T03:32:25.144403Z",
|
||||
"updated_at": "2025-02-13T03:32:25.144403Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Show a Specific ACL
|
||||
|
||||
```bash
|
||||
GET /api/acls/:id
|
||||
```
|
||||
|
||||
- **Description:** Fetches a single ACL by ID, with its members preloaded.
|
||||
- **Authentication:** Requires ACL API Token.
|
||||
- **Parameters:**
|
||||
- `id` (required) — the ACL ID.
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer <REDACTED_TOKEN>" \
|
||||
"https://wanderer.example.com/api/acls/19712899-ec3a-47b1-b73b-2bae221c5513"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "19712899-ec3a-47b1-b73b-2bae221c5513",
|
||||
"name": "aclName",
|
||||
"description": null,
|
||||
"owner_id": "d43a9083-2705-40c9-a314-f7f412346661",
|
||||
"api_key": "REDACTED_API_KEY",
|
||||
"inserted_at": "2025-02-13T03:32:25.144403Z",
|
||||
"updated_at": "2025-02-13T03:32:25.144403Z",
|
||||
"members": [
|
||||
{
|
||||
"id": "8d63ab1e-b44f-4e81-8227-8fb8d928dad8",
|
||||
"name": "Character Name",
|
||||
"role": "admin",
|
||||
"eve_character_id": "2122019111",
|
||||
"inserted_at": "2025-02-13T03:33:32.332598Z",
|
||||
"updated_at": "2025-02-13T03:33:36.644520Z"
|
||||
},
|
||||
{
|
||||
"id": "7e52ab1e-c33f-5e81-9338-7fb8d928ebc9",
|
||||
"name": "Corporation Name",
|
||||
"role": "viewer",
|
||||
"eve_corporation_id": "98140648",
|
||||
"inserted_at": "2025-02-13T03:33:32.332598Z",
|
||||
"updated_at": "2025-02-13T03:33:36.644520Z"
|
||||
},
|
||||
{
|
||||
"id": "6f41bc2f-d44e-6f92-8449-8ec9e039fad7",
|
||||
"name": "Alliance Name",
|
||||
"role": "viewer",
|
||||
"eve_alliance_id": "99013806",
|
||||
"inserted_at": "2025-02-13T03:33:32.332598Z",
|
||||
"updated_at": "2025-02-13T03:33:36.644520Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The response for each member will include only one of `eve_character_id`, `eve_corporation_id`, or `eve_alliance_id` depending on the type of member.
|
||||
|
||||
### 4. Create a New ACL
|
||||
|
||||
```bash
|
||||
POST /api/map/acls
|
||||
```
|
||||
|
||||
- **Description:** Creates a new ACL for a map and generates a new ACL API key.
|
||||
- **Authentication:** Requires Map API Token.
|
||||
- **Required Query Parameter:** Either `map_id` (UUID) or `slug` (map slug).
|
||||
- **Request Body Example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"acl": {
|
||||
"name": "New ACL",
|
||||
"description": "Optional description",
|
||||
"owner_eve_id": "EXTERNAL_EVE_ID"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `owner_eve_id` must be the external EVE id (the `eve_id` from `/api/characters`).
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer <MAP_API_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"acl": {
|
||||
"name": "New ACL",
|
||||
"description": "Optional description",
|
||||
"owner_eve_id": "EXTERNAL_EVE_ID"
|
||||
}
|
||||
}' \
|
||||
"https://wanderer.example.com/api/map/acls?slug=mapname"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "NEW_ACL_UUID",
|
||||
"name": "New ACL",
|
||||
"description": "Optional description",
|
||||
"owner_id": "OWNER_ID",
|
||||
"api_key": "GENERATED_ACL_API_KEY",
|
||||
"inserted_at": "2025-02-14T17:00:00Z",
|
||||
"updated_at": "2025-02-14T17:00:00Z",
|
||||
"members": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Update an ACL
|
||||
|
||||
```bash
|
||||
PUT /api/acls/:id
|
||||
```
|
||||
|
||||
- **Description:** Updates an existing ACL (e.g., name, description).
|
||||
- **Authentication:** Requires ACL API Token.
|
||||
- **Parameters:**
|
||||
- `id` (required) — the ACL ID.
|
||||
- **Request Body Example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"acl": {
|
||||
"name": "Updated ACL Name",
|
||||
"description": "This is the updated description"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -X PUT \
|
||||
-H "Authorization: Bearer <ACL_API_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"acl": {
|
||||
"name": "Updated ACL Name",
|
||||
"description": "This is the updated description"
|
||||
}
|
||||
}' \
|
||||
"https://wanderer.example.com/api/acls/ACL_UUID"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "ACL_UUID",
|
||||
"name": "Updated ACL Name",
|
||||
"description": "This is the updated description",
|
||||
"owner_id": "OWNER_ID",
|
||||
"api_key": "ACL_API_KEY",
|
||||
"inserted_at": "2025-02-14T16:49:13.423556Z",
|
||||
"updated_at": "2025-02-14T17:22:51.343784Z",
|
||||
"members": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Add a Member to an ACL
|
||||
|
||||
```bash
|
||||
POST /api/acls/:acl_id/members
|
||||
```
|
||||
|
||||
- **Description:** Adds a new member (character, corporation, or alliance) to the specified ACL.
|
||||
- **Authentication:** Requires ACL API Token.
|
||||
- **Parameters:**
|
||||
- `acl_id` (required) — the ACL ID.
|
||||
- **Request Body Example:**
|
||||
|
||||
For **character** membership:
|
||||
```json
|
||||
{
|
||||
"member": {
|
||||
"eve_character_id": "EXTERNAL_EVE_ID",
|
||||
"role": "viewer"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For **corporation** membership:
|
||||
```json
|
||||
{
|
||||
"member": {
|
||||
"eve_corporation_id": "CORPORATION_ID",
|
||||
"role": "viewer"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For **alliance** membership:
|
||||
```json
|
||||
{
|
||||
"member": {
|
||||
"eve_alliance_id": "ALLIANCE_ID",
|
||||
"role": "viewer"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example Request for Character
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer <ACL_API_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"member": {
|
||||
"eve_character_id": "EXTERNAL_EVE_ID",
|
||||
"role": "viewer"
|
||||
}
|
||||
}' \
|
||||
"https://wanderer.example.com/api/acls/ACL_UUID/members"
|
||||
```
|
||||
|
||||
#### Example Response for Character
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "MEMBERSHIP_UUID",
|
||||
"name": "Character Name",
|
||||
"role": "viewer",
|
||||
"eve_character_id": "EXTERNAL_EVE_ID",
|
||||
"inserted_at": "2025-02-15T12:30:45.123456Z",
|
||||
"updated_at": "2025-02-15T12:30:45.123456Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example Request for Corporation
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer <ACL_API_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"member": {
|
||||
"eve_corporation_id": "CORPORATION_ID",
|
||||
"role": "viewer"
|
||||
}
|
||||
}' \
|
||||
"https://wanderer.example.com/api/acls/ACL_UUID/members"
|
||||
```
|
||||
|
||||
#### Example Response for Corporation
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "MEMBERSHIP_UUID",
|
||||
"name": "Corporation Name",
|
||||
"role": "viewer",
|
||||
"eve_corporation_id": "CORPORATION_ID",
|
||||
"inserted_at": "2025-02-15T12:30:45.123456Z",
|
||||
"updated_at": "2025-02-15T12:30:45.123456Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example Request for Alliance
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer <ACL_API_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"member": {
|
||||
"eve_alliance_id": "ALLIANCE_ID",
|
||||
"role": "viewer"
|
||||
}
|
||||
}' \
|
||||
"https://wanderer.example.com/api/acls/ACL_UUID/members"
|
||||
```
|
||||
|
||||
#### Example Response for Alliance
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "MEMBERSHIP_UUID",
|
||||
"name": "Alliance Name",
|
||||
"role": "viewer",
|
||||
"eve_alliance_id": "ALLIANCE_ID",
|
||||
"inserted_at": "2025-02-15T12:30:45.123456Z",
|
||||
"updated_at": "2025-02-15T12:30:45.123456Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The response will include only one of `eve_character_id`, `eve_corporation_id`, or `eve_alliance_id` depending on the type of member being added.
|
||||
|
||||
### 7. Change a Member's Role
|
||||
|
||||
```bash
|
||||
PUT /api/acls/:acl_id/members/:member_id
|
||||
```
|
||||
|
||||
- **Description:** Updates an ACL member's role.
|
||||
- **Authentication:** Requires ACL API Token.
|
||||
- **Parameters:**
|
||||
- `acl_id` (required) — the ACL ID.
|
||||
- `member_id` (required) — the external EVE id (or corp/alliance id) used when creating the membership.
|
||||
- **Request Body Example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"member": {
|
||||
"role": "admin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -X PUT \
|
||||
-H "Authorization: Bearer <ACL_API_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"member": {
|
||||
"role": "admin"
|
||||
}
|
||||
}' \
|
||||
"https://wanderer.example.com/api/acls/ACL_UUID/members/EXTERNAL_EVE_ID"
|
||||
```
|
||||
|
||||
#### Example Response for Character
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"id": "MEMBERSHIP_UUID",
|
||||
"name": "Character Name",
|
||||
"role": "admin",
|
||||
"eve_character_id": "EXTERNAL_EVE_ID",
|
||||
"inserted_at": "2025-02-15T12:30:45.123456Z",
|
||||
"updated_at": "2025-02-15T12:35:22.654321Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The response will include only one of `eve_character_id`, `eve_corporation_id`, or `eve_alliance_id` depending on the type of member being updated.
|
||||
|
||||
### 8. Remove a Member from an ACL
|
||||
|
||||
```bash
|
||||
DELETE /api/acls/:acl_id/members/:member_id
|
||||
```
|
||||
|
||||
- **Description:** Removes the member with the specified external EVE id (or corp/alliance id) from the ACL.
|
||||
- **Authentication:** Requires ACL API Token.
|
||||
- **Parameters:**
|
||||
- `acl_id` (required) — the ACL ID.
|
||||
- `member_id` (required) — the external EVE id (or corp/alliance id) used when creating the membership.
|
||||
|
||||
#### Example Request
|
||||
|
||||
```bash
|
||||
curl -X DELETE \
|
||||
-H "Authorization: Bearer <ACL_API_TOKEN>" \
|
||||
"https://wanderer.example.com/api/acls/ACL_UUID/members/EXTERNAL_EVE_ID"
|
||||
```
|
||||
|
||||
#### Example Response
|
||||
|
||||
```json
|
||||
{ "ok": true }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
This guide provides a comprehensive overview of Wanderer's API capabilities. With these endpoints, you can:
|
||||
|
||||
1. **Explore the API** using interactive documentation at `/swaggerui`
|
||||
2. **Retrieve map data** including systems, characters, and kill activity
|
||||
3. **Access system information** with or without authentication
|
||||
4. **Manage Access Control Lists (ACLs)** for permissions
|
||||
5. **Add, update, and remove ACL members** with different roles
|
||||
|
||||
For the most up-to-date and interactive documentation, we recommend using the Swagger UI at `/swaggerui` which allows you to explore and test endpoints directly from your browser.
|
||||
|
||||
If you have any questions or need assistance with the API, please reach out to the Wanderer Team.
|
||||
|
||||
Fly safe,
|
||||
**WANDERER TEAM**
|
||||
13
test/manual/.api_test_config
Normal file
13
test/manual/.api_test_config
Normal file
@@ -0,0 +1,13 @@
|
||||
# Wanderer API Testing Tool Configuration
|
||||
# Generated on Thu Mar 6 14:52:00 UTC 2025
|
||||
|
||||
# Base configuration
|
||||
HOST="http://localhost:4444"
|
||||
MAP_SLUG="flygd"
|
||||
MAP_API_KEY="589016d9-c9ac-48ef-ae74-7a55483b3cc2"
|
||||
ACL_API_KEY=""
|
||||
|
||||
# Selected IDs
|
||||
SELECTED_ACL_ID=""
|
||||
SELECTED_SYSTEM_ID=""
|
||||
CHARACTER_EVE_ID=""
|
||||
13
test/manual/.api_test_config.example
Normal file
13
test/manual/.api_test_config.example
Normal file
@@ -0,0 +1,13 @@
|
||||
# Wanderer API Testing Tool Configuration
|
||||
# Example configuration file - Copy to .api_test_config and modify as needed
|
||||
|
||||
# Base configuration
|
||||
HOST="http://localhost:4000"
|
||||
MAP_SLUG="flygd"
|
||||
MAP_API_KEY="589016d9-c9ac-48ef-ae74-7a55483b3cc2"
|
||||
ACL_API_KEY="acl-api-key-here"
|
||||
|
||||
# Selected IDs
|
||||
SELECTED_ACL_ID="123"
|
||||
SELECTED_SYSTEM_ID="31002019"
|
||||
CHARACTER_EVE_ID="456"
|
||||
13
test/manual/.auto_api_test_config
Normal file
13
test/manual/.auto_api_test_config
Normal file
@@ -0,0 +1,13 @@
|
||||
# Wanderer API Testing Tool Configuration
|
||||
# Generated on Thu Mar 6 18:44:20 UTC 2025
|
||||
|
||||
# Base configuration
|
||||
HOST="http://localhost:4444"
|
||||
MAP_SLUG="flygd"
|
||||
MAP_API_KEY="589016d9-c9ac-48ef-ae74-7a55483b3cc2"
|
||||
ACL_API_KEY="116bd70e-2bbf-4a99-97ed-1869c09ab5bf"
|
||||
|
||||
# Selected IDs
|
||||
SELECTED_ACL_ID="9c91d283-f49f-4f45-a21d-9bf53ce9d1fd"
|
||||
SELECTED_SYSTEM_ID="30002768"
|
||||
CHARACTER_EVE_ID="2115754172"
|
||||
894
test/manual/auto_test_api.sh
Executable file
894
test/manual/auto_test_api.sh
Executable file
@@ -0,0 +1,894 @@
|
||||
#!/bin/bash
|
||||
#==============================================================================
|
||||
# Wanderer API Automated Testing Tool
|
||||
#
|
||||
# This script tests various endpoints of the Wanderer API.
|
||||
#
|
||||
# Features:
|
||||
# - Uses strict mode (set -euo pipefail) for robust error handling.
|
||||
# - Contains a DEBUG mode for extra logging (set DEBUG=1 to enable).
|
||||
# - Validates configuration including a reachability test for the HOST.
|
||||
# - Outputs a summary in plain text and optionally as JSON.
|
||||
# - Exits with a nonzero code if any test fails.
|
||||
#
|
||||
# Usage:
|
||||
# ./auto_test_api.sh
|
||||
#
|
||||
#==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
# Set DEBUG=1 to enable extra logging
|
||||
DEBUG=0
|
||||
# Set VERBOSE=1 to print raw JSON responses for every test (default 0)
|
||||
VERBOSE=0
|
||||
# Set VERBOSE_SUMMARY=1 to output a JSON summary at the end (default 0)
|
||||
VERBOSE_SUMMARY=0
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration file and default configuration
|
||||
CONFIG_FILE=".auto_api_test_config"
|
||||
HOST="http://localhost:4444" # Default host
|
||||
MAP_SLUG=""
|
||||
MAP_API_KEY=""
|
||||
ACL_API_KEY=""
|
||||
SELECTED_ACL_ID=""
|
||||
SELECTED_SYSTEM_ID=""
|
||||
CHARACTER_EVE_ID=""
|
||||
TEST_RESULTS=()
|
||||
FAILED_TESTS=()
|
||||
|
||||
# Global variables for last API response
|
||||
LAST_JSON_RESPONSE=""
|
||||
LAST_HTTP_CODE=""
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Helper Functions
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
debug() {
|
||||
if [ "$DEBUG" -eq 1 ]; then
|
||||
echo -e "${YELLOW}[DEBUG] $*${NC}" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
print_header() {
|
||||
echo -e "\n${BLUE}=== $1 ===${NC}\n"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}$1${NC}"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}$1${NC}" >&2
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}$1${NC}"
|
||||
}
|
||||
|
||||
# Check if the host is reachable; accept any HTTP status code 200-399.
|
||||
check_host_reachable() {
|
||||
debug "Checking if host $HOST is reachable..."
|
||||
local status
|
||||
status=$(curl -s -o /dev/null -w "%{http_code}" "$HOST")
|
||||
debug "HTTP status code for host: $status"
|
||||
if [[ "$status" -ge 200 && "$status" -lt 400 ]]; then
|
||||
print_success "Host $HOST is reachable."
|
||||
else
|
||||
print_error "Host $HOST is not reachable (HTTP code: $status). Please check the host URL."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Load configuration from file
|
||||
load_config() {
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
print_success "Loading configuration from $CONFIG_FILE"
|
||||
source "$CONFIG_FILE"
|
||||
return 0
|
||||
else
|
||||
print_warning "No configuration file found. Using default values."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Save configuration to file
|
||||
save_config() {
|
||||
print_success "Saving configuration to $CONFIG_FILE"
|
||||
cat > "$CONFIG_FILE" << EOF
|
||||
# Wanderer API Testing Tool Configuration
|
||||
# Generated on $(date)
|
||||
|
||||
# Base configuration
|
||||
HOST="$HOST"
|
||||
MAP_SLUG="$MAP_SLUG"
|
||||
MAP_API_KEY="$MAP_API_KEY"
|
||||
ACL_API_KEY="$ACL_API_KEY"
|
||||
|
||||
# Selected IDs
|
||||
SELECTED_ACL_ID="$SELECTED_ACL_ID"
|
||||
SELECTED_SYSTEM_ID="$SELECTED_SYSTEM_ID"
|
||||
CHARACTER_EVE_ID="$CHARACTER_EVE_ID"
|
||||
EOF
|
||||
chmod 600 "$CONFIG_FILE"
|
||||
print_success "Configuration saved successfully."
|
||||
}
|
||||
|
||||
# Make an API call using curl and capture response and HTTP code
|
||||
call_api() {
|
||||
local method=$1
|
||||
local endpoint=$2
|
||||
local api_key=$3
|
||||
local data=${4:-""}
|
||||
|
||||
local curl_cmd=(curl -s -w "\n%{http_code}" -X "$method" -H "Content-Type: application/json")
|
||||
if [ -n "$api_key" ]; then
|
||||
curl_cmd+=(-H "Authorization: Bearer $api_key")
|
||||
fi
|
||||
if [ -n "$data" ]; then
|
||||
curl_cmd+=(-d "$data")
|
||||
fi
|
||||
curl_cmd+=("$HOST$endpoint")
|
||||
|
||||
# Print debug command (mask API key)
|
||||
local debug_cmd
|
||||
debug_cmd=$(printf "%q " "${curl_cmd[@]}")
|
||||
debug_cmd=$(echo "$debug_cmd" | sed "s/$api_key/API_KEY_HIDDEN/g")
|
||||
print_warning "Executing: $debug_cmd"
|
||||
|
||||
local output
|
||||
output=$("${curl_cmd[@]}")
|
||||
LAST_HTTP_CODE=$(echo "$output" | tail -n1)
|
||||
local response
|
||||
response=$(echo "$output" | sed '$d')
|
||||
echo "$response"
|
||||
}
|
||||
|
||||
# Check that required variables are set
|
||||
check_required_vars() {
|
||||
local missing=false
|
||||
if [ $# -eq 0 ]; then
|
||||
if [ -z "$HOST" ]; then
|
||||
print_error "HOST is not set. Please set it first."
|
||||
missing=true
|
||||
fi
|
||||
if [ -z "$MAP_SLUG" ]; then
|
||||
print_error "MAP_SLUG is not set. Please set it first."
|
||||
missing=true
|
||||
fi
|
||||
if [ -z "$MAP_API_KEY" ]; then
|
||||
print_error "MAP_API_KEY is not set. Please set it first."
|
||||
missing=true
|
||||
fi
|
||||
else
|
||||
for var in "$@"; do
|
||||
if [ -z "${!var}" ]; then
|
||||
print_error "$var is not set. Please set it first."
|
||||
missing=true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
$missing && return 1 || return 0
|
||||
}
|
||||
|
||||
# Record a test result
|
||||
record_test_result() {
|
||||
local endpoint=$1
|
||||
local status=$2
|
||||
local message=$3
|
||||
if [ "$status" = "success" ]; then
|
||||
TEST_RESULTS+=("${GREEN}✓${NC} $endpoint - $message")
|
||||
else
|
||||
TEST_RESULTS+=("${RED}✗${NC} $endpoint - $message")
|
||||
FAILED_TESTS+=("$endpoint - $message")
|
||||
fi
|
||||
}
|
||||
|
||||
# Process and validate the JSON response
|
||||
check_response() {
|
||||
local response=$1
|
||||
local endpoint=$2
|
||||
|
||||
if [ -z "$(echo "$response" | xargs)" ]; then
|
||||
if [ "$LAST_HTTP_CODE" = "200" ] || [ "$LAST_HTTP_CODE" = "204" ]; then
|
||||
print_success "Received empty response, which is valid"
|
||||
LAST_JSON_RESPONSE="{}"
|
||||
return 0
|
||||
else
|
||||
record_test_result "$endpoint" "failure" "Empty response with HTTP code $LAST_HTTP_CODE"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
echo "Raw response from $endpoint:"
|
||||
echo "$response" | head -n 20
|
||||
fi
|
||||
|
||||
if echo "$response" | jq . > /dev/null 2>&1; then
|
||||
LAST_JSON_RESPONSE="$response"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local json_part
|
||||
json_part=$(echo "$response" | grep -o '{.*}' || echo "")
|
||||
if [ -z "$json_part" ] || ! echo "$json_part" | jq . > /dev/null 2>&1; then
|
||||
json_part=$(echo "$response" | sed -n '/^{/,$p' | tr -d '\n')
|
||||
fi
|
||||
if [ -z "$json_part" ] || ! echo "$json_part" | jq . > /dev/null 2>&1; then
|
||||
json_part=$(echo "$response" | sed -n '/{/,/}/p' | tr -d '\n')
|
||||
fi
|
||||
if [ -z "$json_part" ] || ! echo "$json_part" | jq . > /dev/null 2>&1; then
|
||||
json_part=$(echo "$response" | awk '!(/^[<>*]/) {print}' | tr -d '\n')
|
||||
fi
|
||||
if [ -z "$json_part" ] || ! echo "$json_part" | jq . > /dev/null 2>&1; then
|
||||
echo "Raw response from $endpoint:"
|
||||
echo "$response"
|
||||
record_test_result "$endpoint" "failure" "Invalid JSON response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local error
|
||||
error=$(echo "$json_part" | jq -r '.error // empty')
|
||||
if [ -n "$error" ]; then
|
||||
echo "Raw response from $endpoint:"
|
||||
echo "$response"
|
||||
echo "Parsed JSON response from $endpoint:"
|
||||
echo "$json_part" | jq '.'
|
||||
record_test_result "$endpoint" "failure" "Error: $error"
|
||||
return 1
|
||||
fi
|
||||
|
||||
LAST_JSON_RESPONSE="$json_part"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Get a random item from a JSON array using a jq path
|
||||
get_random_item() {
|
||||
local json=$1
|
||||
local jq_path=$2
|
||||
local count
|
||||
count=$(echo "$json" | jq "$jq_path | length")
|
||||
if [ "$count" -eq 0 ]; then
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
local random_index=$((RANDOM % count))
|
||||
echo "$json" | jq -r "$jq_path[$random_index]"
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# API Test Functions
|
||||
#------------------------------------------------------------------------------
|
||||
test_list_characters() {
|
||||
print_header "Testing GET /api/characters"
|
||||
print_success "Calling API: GET /api/characters"
|
||||
local response
|
||||
response=$(call_api "GET" "/api/characters" "$MAP_API_KEY")
|
||||
if ! check_response "$response" "GET /api/characters"; then
|
||||
return 1
|
||||
fi
|
||||
local character_count
|
||||
character_count=$(echo "$LAST_JSON_RESPONSE" | jq '.data | length')
|
||||
if [ "$character_count" -gt 0 ]; then
|
||||
record_test_result "GET /api/characters" "success" "Found $character_count characters"
|
||||
if [ -z "$CHARACTER_EVE_ID" ]; then
|
||||
local random_index=$((RANDOM % character_count))
|
||||
print_success "Selecting character at index $random_index"
|
||||
local random_character
|
||||
random_character=$(echo "$LAST_JSON_RESPONSE" | jq ".data[$random_index]")
|
||||
CHARACTER_EVE_ID=$(echo "$random_character" | jq -r '.eve_id')
|
||||
local character_name
|
||||
character_name=$(echo "$random_character" | jq -r '.name')
|
||||
print_success "Selected random character: $character_name (EVE ID: $CHARACTER_EVE_ID)"
|
||||
fi
|
||||
return 0
|
||||
else
|
||||
record_test_result "GET /api/characters" "success" "No characters found"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
test_map_systems() {
|
||||
print_header "Testing GET /api/map/systems"
|
||||
if ! check_required_vars "MAP_SLUG" "MAP_API_KEY"; then
|
||||
record_test_result "GET /api/map/systems" "failure" "Missing required variables"
|
||||
return 1
|
||||
fi
|
||||
print_success "Calling API: GET /api/map/systems?slug=$MAP_SLUG"
|
||||
local response
|
||||
response=$(call_api "GET" "/api/map/systems?slug=$MAP_SLUG" "$MAP_API_KEY")
|
||||
if ! check_response "$response" "GET /api/map/systems"; then
|
||||
return 1
|
||||
fi
|
||||
local system_count
|
||||
system_count=$(echo "$LAST_JSON_RESPONSE" | jq '.data | length')
|
||||
print_success "System count: $system_count"
|
||||
if [ "$system_count" -gt 0 ]; then
|
||||
record_test_result "GET /api/map/systems" "success" "Found $system_count systems"
|
||||
local random_index=$((RANDOM % system_count))
|
||||
print_success "Selecting system at index $random_index"
|
||||
echo "Data structure:"
|
||||
echo "$LAST_JSON_RESPONSE" | jq '.data[0]'
|
||||
local random_system
|
||||
random_system=$(echo "$LAST_JSON_RESPONSE" | jq ".data[$random_index]")
|
||||
echo "Selected system JSON:"
|
||||
echo "$random_system"
|
||||
SELECTED_SYSTEM_ID=$(echo "$random_system" | jq -r '.solar_system_id')
|
||||
if [ -z "$SELECTED_SYSTEM_ID" ] || [ "$SELECTED_SYSTEM_ID" = "null" ]; then
|
||||
SELECTED_SYSTEM_ID=$(echo "$random_system" | jq -r '.id // .system_id // empty')
|
||||
if [ -z "$SELECTED_SYSTEM_ID" ] || [ "$SELECTED_SYSTEM_ID" = "null" ]; then
|
||||
print_error "Could not find system ID in the response"
|
||||
echo "Available fields:"
|
||||
echo "$random_system" | jq 'keys'
|
||||
record_test_result "GET /api/map/systems" "failure" "Could not extract system ID"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
local system_name
|
||||
system_name=$(echo "$random_system" | jq -r '.name // "Unknown"')
|
||||
print_success "Selected random system: $system_name (ID: $SELECTED_SYSTEM_ID)"
|
||||
return 0
|
||||
else
|
||||
record_test_result "GET /api/map/systems" "failure" "No systems found"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_map_system() {
|
||||
print_header "Testing GET /api/map/system"
|
||||
if [[ -z "$MAP_SLUG" || -z "$SELECTED_SYSTEM_ID" || -z "$MAP_API_KEY" ]]; then
|
||||
record_test_result "GET /api/map/system" "failure" "Missing required variables"
|
||||
return
|
||||
fi
|
||||
local response
|
||||
response=$(call_api "GET" "/api/map/system?slug=$MAP_SLUG&id=$SELECTED_SYSTEM_ID" "$MAP_API_KEY")
|
||||
print_warning "Response: $response"
|
||||
local trimmed_response
|
||||
trimmed_response=$(echo "$response" | xargs)
|
||||
if [[ "$trimmed_response" == "{}" || "$trimmed_response" == '{"data":{}}' ]]; then
|
||||
print_success "Received empty JSON response, which is valid"
|
||||
record_test_result "GET /api/map/system" "success" "Received valid empty response"
|
||||
return
|
||||
fi
|
||||
if ! check_response "$response" "GET /api/map/system"; then
|
||||
return
|
||||
fi
|
||||
local json_data="$LAST_JSON_RESPONSE"
|
||||
local has_data
|
||||
has_data=$(echo "$json_data" | jq 'has("data")')
|
||||
if [ "$has_data" != "true" ]; then
|
||||
print_error "Response does not contain 'data' field"
|
||||
echo "JSON Response:"
|
||||
echo "$json_data" | jq .
|
||||
record_test_result "GET /api/map/system" "failure" "Response does not contain 'data' field"
|
||||
return
|
||||
fi
|
||||
local system_data
|
||||
system_data=$(echo "$json_data" | jq -r '.data // empty')
|
||||
if [ -z "$system_data" ] || [ "$system_data" = "null" ]; then
|
||||
print_error "Could not find system data in response"
|
||||
echo "JSON Response:"
|
||||
echo "$json_data" | jq .
|
||||
record_test_result "GET /api/map/system" "failure" "Could not find system data in response"
|
||||
return
|
||||
fi
|
||||
local system_id
|
||||
system_id=$(echo "$json_data" | jq -r '.data.solar_system_id // empty')
|
||||
if [ -z "$system_id" ] || [ "$system_id" = "null" ]; then
|
||||
print_error "Could not find solar_system_id in the system data"
|
||||
echo "System Data:"
|
||||
echo "$system_data" | jq .
|
||||
record_test_result "GET /api/map/system" "failure" "Could not find solar_system_id in system data"
|
||||
return
|
||||
fi
|
||||
print_success "Found system data with ID: $system_id"
|
||||
record_test_result "GET /api/map/system" "success" "Found system data with ID: $system_id"
|
||||
}
|
||||
|
||||
test_map_characters() {
|
||||
print_header "Testing GET /api/map/characters"
|
||||
if ! check_required_vars "MAP_SLUG" "MAP_API_KEY"; then
|
||||
record_test_result "GET /api/map/characters" "failure" "Missing required variables"
|
||||
return 1
|
||||
fi
|
||||
print_success "Calling API: GET /api/map/characters?slug=$MAP_SLUG"
|
||||
local response
|
||||
response=$(call_api "GET" "/api/map/characters?slug=$MAP_SLUG" "$MAP_API_KEY")
|
||||
if ! check_response "$response" "GET /api/map/characters"; then
|
||||
return 1
|
||||
fi
|
||||
local character_count
|
||||
character_count=$(echo "$LAST_JSON_RESPONSE" | jq '.data | length')
|
||||
record_test_result "GET /api/map/characters" "success" "Found $character_count tracked characters"
|
||||
return 0
|
||||
}
|
||||
|
||||
test_map_structure_timers() {
|
||||
print_header "Testing GET /api/map/structure-timers"
|
||||
if [[ -z "$MAP_SLUG" || -z "$MAP_API_KEY" ]]; then
|
||||
record_test_result "GET /api/map/structure-timers" "failure" "Missing required variables"
|
||||
return
|
||||
fi
|
||||
local response
|
||||
response=$(call_api "GET" "/api/map/structure-timers?slug=$MAP_SLUG" "$MAP_API_KEY")
|
||||
local trimmed_response
|
||||
trimmed_response=$(echo "$response" | xargs)
|
||||
if [[ "$trimmed_response" == '{"data":[]}' ]]; then
|
||||
print_success "Found 0 structure timers"
|
||||
record_test_result "GET /api/map/structure-timers" "success" "Found 0 structure timers"
|
||||
fi
|
||||
if ! check_response "$response" "GET /api/map/structure-timers"; then
|
||||
return
|
||||
fi
|
||||
local timer_count
|
||||
timer_count=$(echo "$LAST_JSON_RESPONSE" | jq '.data | length')
|
||||
print_success "Found $timer_count structure timers"
|
||||
record_test_result "GET /api/map/structure-timers" "success" "Found $timer_count structure timers"
|
||||
if [ -n "$SELECTED_SYSTEM_ID" ]; then
|
||||
print_header "Testing GET /api/map/structure-timers (filtered)"
|
||||
local filtered_response
|
||||
filtered_response=$(call_api "GET" "/api/map/structure-timers?slug=$MAP_SLUG&system_id=$SELECTED_SYSTEM_ID" "$MAP_API_KEY")
|
||||
print_warning "(Structure Timers) - Filtered response: $filtered_response"
|
||||
local trimmed_filtered
|
||||
trimmed_filtered=$(echo "$filtered_response" | xargs)
|
||||
if [[ "$trimmed_filtered" == '{"data":[]}' ]]; then
|
||||
print_success "Found 0 filtered structure timers"
|
||||
record_test_result "GET /api/map/structure-timers (filtered)" "success" "Found 0 filtered structure timers"
|
||||
return
|
||||
fi
|
||||
if ! check_response "$filtered_response" "GET /api/map/structure-timers (filtered)"; then
|
||||
return
|
||||
fi
|
||||
local filtered_count
|
||||
filtered_count=$(echo "$LAST_JSON_RESPONSE" | jq '.data | length')
|
||||
print_success "Found $filtered_count filtered structure timers"
|
||||
record_test_result "GET /api/map/structure-timers (filtered)" "success" "Found $filtered_count filtered structure timers"
|
||||
fi
|
||||
}
|
||||
|
||||
test_map_systems_kills() {
|
||||
print_header "Testing GET /api/map/systems-kills"
|
||||
if [[ -z "$MAP_SLUG" || -z "$MAP_API_KEY" ]]; then
|
||||
record_test_result "GET /api/map/systems-kills" "failure" "Missing required variables"
|
||||
return
|
||||
fi
|
||||
# Use the correct parameter name: hours
|
||||
local response
|
||||
response=$(call_api "GET" "/api/map/systems-kills?slug=$MAP_SLUG&hours=1" "$MAP_API_KEY")
|
||||
print_warning "(Systems Kills) - Response: $response"
|
||||
if ! check_response "$response" "GET /api/map/systems-kills"; then
|
||||
return
|
||||
fi
|
||||
local json_data="$LAST_JSON_RESPONSE"
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
echo "JSON Response:"; echo "$json_data" | jq .
|
||||
fi
|
||||
local has_data
|
||||
has_data=$(echo "$json_data" | jq 'has("data")')
|
||||
if [ "$has_data" != "true" ]; then
|
||||
print_error "Response does not contain 'data' field"
|
||||
if [ "$VERBOSE" -eq 1 ]; then
|
||||
echo "JSON Response:"; echo "$json_data" | jq .
|
||||
fi
|
||||
record_test_result "GET /api/map/systems-kills" "failure" "Response does not contain 'data' field"
|
||||
return
|
||||
fi
|
||||
local systems_count
|
||||
systems_count=$(echo "$json_data" | jq '.data | length')
|
||||
print_success "Found kill data for $systems_count systems"
|
||||
record_test_result "GET /api/map/systems-kills" "success" "Found kill data for $systems_count systems"
|
||||
print_header "Testing GET /api/map/systems-kills (filtered)"
|
||||
local filter_url="/api/map/systems-kills?slug=$MAP_SLUG&hours=1"
|
||||
if [ -n "$SELECTED_SYSTEM_ID" ]; then
|
||||
filter_url="$filter_url&system_id=$SELECTED_SYSTEM_ID"
|
||||
print_success "Using system_id filter to reduce response size"
|
||||
fi
|
||||
local filtered_response
|
||||
filtered_response=$(call_api "GET" "$filter_url" "$MAP_API_KEY")
|
||||
local trimmed_filtered
|
||||
trimmed_filtered=$(echo "$filtered_response" | xargs)
|
||||
if [[ "$trimmed_filtered" == '{"data":[]}' ]]; then
|
||||
print_success "Found 0 filtered systems with kill data"
|
||||
record_test_result "GET /api/map/systems-kills (filtered)" "success" "Found 0 filtered systems with kill data"
|
||||
return
|
||||
fi
|
||||
if [[ "$trimmed_filtered" == '{"data":'* ]]; then
|
||||
print_success "Received valid JSON response (large data)"
|
||||
record_test_result "GET /api/map/systems-kills (filtered)" "success" "Received valid JSON response with kill data"
|
||||
return
|
||||
fi
|
||||
if ! check_response "$filtered_response" "GET /api/map/systems-kills (filtered)"; then
|
||||
return
|
||||
fi
|
||||
local filtered_count
|
||||
filtered_count=$(echo "$LAST_JSON_RESPONSE" | jq '.data | length')
|
||||
print_success "Found filtered kill data for $filtered_count systems"
|
||||
record_test_result "GET /api/map/systems-kills (filtered)" "success" "Found filtered kill data for $filtered_count systems"
|
||||
}
|
||||
|
||||
test_map_acls() {
|
||||
print_header "Testing GET /api/map/acls"
|
||||
if ! check_required_vars "MAP_SLUG" "MAP_API_KEY"; then
|
||||
record_test_result "GET /api/map/acls" "failure" "Missing required variables"
|
||||
return 1
|
||||
fi
|
||||
print_success "Calling API: GET /api/map/acls?slug=$MAP_SLUG"
|
||||
local response
|
||||
response=$(call_api "GET" "/api/map/acls?slug=$MAP_SLUG" "$MAP_API_KEY")
|
||||
if ! check_response "$response" "GET /api/map/acls"; then
|
||||
return 1
|
||||
fi
|
||||
local acl_count
|
||||
acl_count=$(echo "$LAST_JSON_RESPONSE" | jq '.data | length')
|
||||
record_test_result "GET /api/map/acls" "success" "Found $acl_count ACLs"
|
||||
if [ "$acl_count" -gt 0 ]; then
|
||||
local random_acl
|
||||
random_acl=$(get_random_item "$LAST_JSON_RESPONSE" ".data")
|
||||
SELECTED_ACL_ID=$(echo "$random_acl" | jq -r '.id')
|
||||
local acl_name
|
||||
acl_name=$(echo "$random_acl" | jq -r '.name')
|
||||
print_success "Selected random ACL: $acl_name (ID: $SELECTED_ACL_ID)"
|
||||
else
|
||||
print_warning "No ACLs found to select for future tests"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
test_create_acl() {
|
||||
print_header "Testing POST /api/map/acls"
|
||||
if ! check_required_vars "MAP_SLUG" "MAP_API_KEY"; then
|
||||
record_test_result "POST /api/map/acls" "failure" "Missing required variables"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$CHARACTER_EVE_ID" ]; then
|
||||
print_warning "No character EVE ID selected. Fetching characters..."
|
||||
print_success "Calling API: GET /api/characters"
|
||||
local characters_response
|
||||
characters_response=$(call_api "GET" "/api/characters" "$MAP_API_KEY")
|
||||
if ! check_response "$characters_response" "GET /api/characters"; then
|
||||
record_test_result "POST /api/map/acls" "failure" "Failed to get characters"
|
||||
return 1
|
||||
fi
|
||||
local character_count
|
||||
character_count=$(echo "$LAST_JSON_RESPONSE" | jq '.data | length')
|
||||
if [ "$character_count" -eq 0 ]; then
|
||||
record_test_result "POST /api/map/acls" "failure" "No characters found"
|
||||
return 1
|
||||
fi
|
||||
local random_index=$((RANDOM % character_count))
|
||||
print_success "Selecting character at index $random_index"
|
||||
local random_character
|
||||
random_character=$(echo "$LAST_JSON_RESPONSE" | jq ".data[$random_index]")
|
||||
CHARACTER_EVE_ID=$(echo "$random_character" | jq -r '.eve_id')
|
||||
local character_name
|
||||
character_name=$(echo "$random_character" | jq -r '.name')
|
||||
print_success "Selected random character: $character_name (EVE ID: $CHARACTER_EVE_ID)"
|
||||
fi
|
||||
local acl_name="Auto Test ACL $(date +%s)"
|
||||
local acl_description="Created by auto_test_api.sh on $(date)"
|
||||
local data="{\"acl\": {\"name\": \"$acl_name\", \"owner_eve_id\": $CHARACTER_EVE_ID, \"description\": \"$acl_description\"}}"
|
||||
print_success "Calling API: POST /api/map/acls?slug=$MAP_SLUG"
|
||||
print_success "Data: $data"
|
||||
local response
|
||||
response=$(call_api "POST" "/api/map/acls?slug=$MAP_SLUG" "$MAP_API_KEY" "$data")
|
||||
if ! check_response "$response" "POST /api/map/acls"; then
|
||||
return 1
|
||||
fi
|
||||
local new_acl_id
|
||||
new_acl_id=$(echo "$LAST_JSON_RESPONSE" | jq -r '.data.id // empty')
|
||||
local new_api_key
|
||||
new_api_key=$(echo "$LAST_JSON_RESPONSE" | jq -r '.data.api_key // empty')
|
||||
if [ -n "$new_acl_id" ] && [ -n "$new_api_key" ]; then
|
||||
record_test_result "POST /api/map/acls" "success" "Created new ACL with ID: $new_acl_id"
|
||||
SELECTED_ACL_ID=$new_acl_id
|
||||
ACL_API_KEY=$new_api_key
|
||||
print_success "Using the new ACL (ID: $SELECTED_ACL_ID) and its API key for further operations"
|
||||
save_config
|
||||
return 0
|
||||
else
|
||||
record_test_result "POST /api/map/acls" "failure" "Failed to extract ACL ID or API key from response"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_show_acl() {
|
||||
print_header "Testing GET /api/acls/:id"
|
||||
if [ -z "$SELECTED_ACL_ID" ] || [ -z "$ACL_API_KEY" ]; then
|
||||
record_test_result "GET /api/acls/:id" "failure" "Missing ACL ID or API key"
|
||||
return 1
|
||||
fi
|
||||
print_success "Calling API: GET /api/acls/$SELECTED_ACL_ID"
|
||||
local response
|
||||
response=$(call_api "GET" "/api/acls/$SELECTED_ACL_ID" "$ACL_API_KEY")
|
||||
if ! check_response "$response" "GET /api/acls/:id"; then
|
||||
return 1
|
||||
fi
|
||||
local acl_name
|
||||
acl_name=$(echo "$LAST_JSON_RESPONSE" | jq -r '.data.name // empty')
|
||||
if [ -n "$acl_name" ]; then
|
||||
record_test_result "GET /api/acls/:id" "success" "Found ACL: $acl_name"
|
||||
return 0
|
||||
else
|
||||
record_test_result "GET /api/acls/:id" "failure" "ACL data not found"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_update_acl() {
|
||||
print_header "Testing PUT /api/acls/:id"
|
||||
if [ -z "$SELECTED_ACL_ID" ] || [ -z "$ACL_API_KEY" ]; then
|
||||
record_test_result "PUT /api/acls/:id" "failure" "Missing ACL ID or API key"
|
||||
return 1
|
||||
fi
|
||||
local new_name="Updated Auto Test ACL $(date +%s)"
|
||||
local new_description="Updated by auto_test_api.sh on $(date)"
|
||||
local data="{\"acl\": {\"name\": \"$new_name\", \"description\": \"$new_description\"}}"
|
||||
print_success "Calling API: PUT /api/acls/$SELECTED_ACL_ID"
|
||||
print_success "Data: $data"
|
||||
local response
|
||||
response=$(call_api "PUT" "/api/acls/$SELECTED_ACL_ID" "$ACL_API_KEY" "$data")
|
||||
if ! check_response "$response" "PUT /api/acls/:id"; then
|
||||
return 1
|
||||
fi
|
||||
local updated_name
|
||||
updated_name=$(echo "$LAST_JSON_RESPONSE" | jq -r '.data.name // empty')
|
||||
if [ "$updated_name" = "$new_name" ]; then
|
||||
record_test_result "PUT /api/acls/:id" "success" "Updated ACL name to: $updated_name"
|
||||
return 0
|
||||
else
|
||||
record_test_result "PUT /api/acls/:id" "failure" "Failed to update ACL name"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_create_acl_member() {
|
||||
print_header "Testing POST /api/acls/:acl_id/members"
|
||||
if [ -z "$SELECTED_ACL_ID" ] || [ -z "$ACL_API_KEY" ]; then
|
||||
record_test_result "POST /api/acls/:acl_id/members" "failure" "Missing ACL ID or API key"
|
||||
return 1
|
||||
fi
|
||||
if [ -z "$CHARACTER_EVE_ID" ]; then
|
||||
print_warning "No character EVE ID selected. Fetching characters..."
|
||||
print_success "Calling API: GET /api/characters"
|
||||
local characters_response
|
||||
characters_response=$(call_api "GET" "/api/characters" "$MAP_API_KEY")
|
||||
if ! check_response "$characters_response" "GET /api/characters"; then
|
||||
record_test_result "POST /api/acls/:acl_id/members" "failure" "Failed to get characters"
|
||||
return 1
|
||||
fi
|
||||
local character_count
|
||||
character_count=$(echo "$LAST_JSON_RESPONSE" | jq '.data | length')
|
||||
if [ "$character_count" -eq 0 ]; then
|
||||
record_test_result "POST /api/acls/:acl_id/members" "failure" "No characters found"
|
||||
return 1
|
||||
fi
|
||||
local random_index=$((RANDOM % character_count))
|
||||
print_success "Selecting character at index $random_index"
|
||||
local random_character
|
||||
random_character=$(echo "$LAST_JSON_RESPONSE" | jq ".data[$random_index]")
|
||||
CHARACTER_EVE_ID=$(echo "$random_character" | jq -r '.eve_id')
|
||||
local character_name
|
||||
character_name=$(echo "$random_character" | jq -r '.name')
|
||||
print_success "Selected random character: $character_name (EVE ID: $CHARACTER_EVE_ID)"
|
||||
fi
|
||||
local data="{\"member\": {\"eve_character_id\": $CHARACTER_EVE_ID, \"role\": \"member\"}}"
|
||||
print_success "Calling API: POST /api/acls/$SELECTED_ACL_ID/members"
|
||||
print_success "Data: $data"
|
||||
local response
|
||||
response=$(call_api "POST" "/api/acls/$SELECTED_ACL_ID/members" "$ACL_API_KEY" "$data")
|
||||
if ! check_response "$response" "POST /api/acls/:acl_id/members"; then
|
||||
return 1
|
||||
fi
|
||||
local member_id
|
||||
member_id=$(echo "$LAST_JSON_RESPONSE" | jq -r '.data.id // empty')
|
||||
if [ -n "$member_id" ]; then
|
||||
record_test_result "POST /api/acls/:acl_id/members" "success" "Created new member with ID: $member_id"
|
||||
MEMBER_ID=$CHARACTER_EVE_ID
|
||||
return 0
|
||||
else
|
||||
record_test_result "POST /api/acls/:acl_id/members" "failure" "Failed to create member"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_update_acl_member() {
|
||||
print_header "Testing PUT /api/acls/:acl_id/members/:member_id"
|
||||
if [ -z "$SELECTED_ACL_ID" ] || [ -z "$ACL_API_KEY" ] || [ -z "$MEMBER_ID" ]; then
|
||||
record_test_result "PUT /api/acls/:acl_id/members/:member_id" "failure" "Missing ACL ID, API key, or member ID"
|
||||
return 1
|
||||
fi
|
||||
local data="{\"member\": {\"role\": \"member\"}}"
|
||||
print_success "Calling API: PUT /api/acls/$SELECTED_ACL_ID/members/$MEMBER_ID"
|
||||
print_success "Data: $data"
|
||||
local response
|
||||
response=$(call_api "PUT" "/api/acls/$SELECTED_ACL_ID/members/$MEMBER_ID" "$ACL_API_KEY" "$data")
|
||||
if ! check_response "$response" "PUT /api/acls/:acl_id/members/:member_id"; then
|
||||
return 1
|
||||
fi
|
||||
local updated_role
|
||||
updated_role=$(echo "$LAST_JSON_RESPONSE" | jq -r '.data.role // empty')
|
||||
if [ "$updated_role" = "member" ]; then
|
||||
record_test_result "PUT /api/acls/:acl_id/members/:member_id" "success" "Updated member role to: $updated_role"
|
||||
return 0
|
||||
else
|
||||
record_test_result "PUT /api/acls/:acl_id/members/:member_id" "failure" "Failed to update member role"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_delete_acl_member() {
|
||||
print_header "Testing DELETE /api/acls/:acl_id/members/:member_id"
|
||||
if [ -z "$SELECTED_ACL_ID" ] || [ -z "$ACL_API_KEY" ] || [ -z "$MEMBER_ID" ]; then
|
||||
record_test_result "DELETE /api/acls/:acl_id/members/:member_id" "failure" "Missing ACL ID, API key, or member ID"
|
||||
return 1
|
||||
fi
|
||||
print_success "Calling API: DELETE /api/acls/$SELECTED_ACL_ID/members/$MEMBER_ID"
|
||||
local response
|
||||
response=$(call_api "DELETE" "/api/acls/$SELECTED_ACL_ID/members/$MEMBER_ID" "$ACL_API_KEY")
|
||||
if ! check_response "$response" "DELETE /api/acls/:acl_id/members/:member_id"; then
|
||||
return 1
|
||||
fi
|
||||
record_test_result "DELETE /api/acls/:acl_id/members/:member_id" "success" "Deleted member with ID: $MEMBER_ID"
|
||||
MEMBER_ID=""
|
||||
return 0
|
||||
}
|
||||
|
||||
test_system_static_info() {
|
||||
print_header "Testing GET /api/common/system-static-info"
|
||||
if [ -z "$SELECTED_SYSTEM_ID" ]; then
|
||||
record_test_result "GET /api/common/system-static-info" "failure" "No system ID selected"
|
||||
return 1
|
||||
fi
|
||||
print_success "Calling API: GET /api/common/system-static-info?id=$SELECTED_SYSTEM_ID"
|
||||
local response
|
||||
response=$(call_api "GET" "/api/common/system-static-info?id=$SELECTED_SYSTEM_ID" "$MAP_API_KEY")
|
||||
if ! check_response "$response" "GET /api/common/system-static-info"; then
|
||||
return 1
|
||||
fi
|
||||
local system_count
|
||||
system_count=$(echo "$LAST_JSON_RESPONSE" | jq 'length')
|
||||
record_test_result "GET /api/common/system-static-info" "success" "Found static info for $system_count systems"
|
||||
return 0
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Configuration and Main Menu Functions
|
||||
#------------------------------------------------------------------------------
|
||||
set_config() {
|
||||
print_header "Configuration"
|
||||
echo -e "Current configuration:"
|
||||
[ -n "$HOST" ] && echo -e " Host: ${BLUE}$HOST${NC}"
|
||||
[ -n "$MAP_SLUG" ] && echo -e " Map Slug: ${BLUE}$MAP_SLUG${NC}"
|
||||
[ -n "$MAP_API_KEY" ] && echo -e " Map API Key: ${BLUE}${MAP_API_KEY:0:8}...${NC}"
|
||||
read -p "Enter host (default: $HOST): " input_host
|
||||
[ -n "$input_host" ] && HOST="$input_host"
|
||||
read -p "Enter map slug: " input_map_slug
|
||||
[ -n "$input_map_slug" ] && MAP_SLUG="$input_map_slug"
|
||||
read -p "Enter map API key: " input_map_api_key
|
||||
[ -n "$input_map_api_key" ] && MAP_API_KEY="$input_map_api_key"
|
||||
# Reset IDs to force fresh data
|
||||
SELECTED_SYSTEM_ID=""
|
||||
SELECTED_ACL_ID=""
|
||||
ACL_API_KEY=""
|
||||
CHARACTER_EVE_ID=""
|
||||
save_config
|
||||
}
|
||||
|
||||
run_all_tests() {
|
||||
print_header "Running all API tests"
|
||||
TEST_RESULTS=()
|
||||
FAILED_TESTS=()
|
||||
|
||||
if ! command -v jq &> /dev/null; then
|
||||
print_error "jq is required for this script to work. Please install it first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! check_required_vars "MAP_SLUG" "MAP_API_KEY"; then
|
||||
print_error "Please set MAP_SLUG and MAP_API_KEY before running tests."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check_host_reachable
|
||||
|
||||
test_list_characters
|
||||
if test_map_systems; then
|
||||
test_map_system
|
||||
else
|
||||
print_error "Skipping test_map_system because test_map_systems failed"
|
||||
record_test_result "GET /api/map/system" "failure" "Skipped because test_map_systems failed"
|
||||
fi
|
||||
test_map_characters
|
||||
test_map_structure_timers
|
||||
test_map_systems_kills
|
||||
test_map_acls
|
||||
if test_create_acl; then
|
||||
test_show_acl
|
||||
test_update_acl
|
||||
if test_create_acl_member; then
|
||||
test_update_acl_member
|
||||
test_delete_acl_member
|
||||
else
|
||||
print_error "Skipping ACL member tests because test_create_acl_member failed"
|
||||
record_test_result "PUT /api/acls/:acl_id/members/:member_id" "failure" "Skipped because test_create_acl_member failed"
|
||||
record_test_result "DELETE /api/acls/:acl_id/members/:member_id" "failure" "Skipped because test_create_acl_member failed"
|
||||
fi
|
||||
else
|
||||
print_error "Skipping ACL tests because test_create_acl failed"
|
||||
record_test_result "GET /api/acls/:id" "failure" "Skipped because test_create_acl failed"
|
||||
record_test_result "PUT /api/acls/:id" "failure" "Skipped because test_create_acl failed"
|
||||
record_test_result "POST /api/acls/:acl_id/members" "failure" "Skipped because test_create_acl failed"
|
||||
record_test_result "PUT /api/acls/:acl_id/members/:member_id" "failure" "Skipped because test_create_acl failed"
|
||||
record_test_result "DELETE /api/acls/:acl_id/members/:member_id" "failure" "Skipped because test_create_acl failed"
|
||||
fi
|
||||
test_system_static_info
|
||||
|
||||
print_header "Test Results"
|
||||
for result in "${TEST_RESULTS[@]}"; do
|
||||
echo -e "$result"
|
||||
done
|
||||
|
||||
local total_tests=${#TEST_RESULTS[@]}
|
||||
local failed_tests=${#FAILED_TESTS[@]}
|
||||
local passed_tests=$((total_tests - failed_tests))
|
||||
print_header "Summary"
|
||||
echo -e "Total tests: $total_tests"
|
||||
echo -e "Passed: ${GREEN}$passed_tests${NC}"
|
||||
echo -e "Failed: ${RED}$failed_tests${NC}"
|
||||
if [ $failed_tests -gt 0 ]; then
|
||||
print_header "Failed Tests"
|
||||
for failed in "${FAILED_TESTS[@]}"; do
|
||||
echo -e "${RED}✗${NC} $failed"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$VERBOSE_SUMMARY" -eq 1 ]; then
|
||||
summary_json=$(jq -n --arg total "$total_tests" --arg passed "$passed_tests" --arg failed "$failed_tests" \
|
||||
'{total_tests: $total_tests|tonumber, passed: $passed|tonumber, failed: $failed|tonumber}')
|
||||
echo "JSON Summary:"; echo "$summary_json" | jq .
|
||||
fi
|
||||
|
||||
save_config
|
||||
|
||||
if [ $failed_tests -gt 0 ]; then
|
||||
exit 1
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Main Menu and Entry Point
|
||||
#------------------------------------------------------------------------------
|
||||
main() {
|
||||
print_header "Wanderer API Automated Testing Tool"
|
||||
load_config
|
||||
if [ -z "$MAP_SLUG" ] || [ -z "$MAP_API_KEY" ]; then
|
||||
print_warning "MAP_SLUG or MAP_API_KEY not set. Let's configure them now."
|
||||
set_config
|
||||
fi
|
||||
echo -e "What would you like to do?"
|
||||
echo "1) Run all tests"
|
||||
echo "2) Set configuration"
|
||||
echo "3) Exit"
|
||||
read -p "Enter your choice: " choice
|
||||
case $choice in
|
||||
1) run_all_tests ;;
|
||||
2) set_config ;;
|
||||
3) exit 0 ;;
|
||||
*) print_error "Invalid choice"; main ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Start the script
|
||||
main
|
||||
@@ -1,198 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Example script to test your Map & ACL endpoints using curl.
|
||||
# Requires `jq` to parse JSON responses.
|
||||
|
||||
# If any command fails, this script will exit immediately
|
||||
set -e
|
||||
|
||||
#############################################
|
||||
# Environment Variables (must be set before)
|
||||
#############################################
|
||||
: "${BASE_URL:?Need to set BASE_URL, e.g. http://localhost:4444}"
|
||||
: "${MAP_TOKEN:?Need to set MAP_TOKEN (Bearer token for map requests)}"
|
||||
: "${MAP_SLUG:?Need to set MAP_SLUG (slug for the map to test)}"
|
||||
: "${EVE_CHARACTER_ID:?Need to set EVE_CHARACTER_ID (e.g. from /api/characters)}"
|
||||
|
||||
echo "Using BASE_URL = $BASE_URL"
|
||||
echo "Using MAP_TOKEN = $MAP_TOKEN"
|
||||
echo "Using MAP_SLUG = $MAP_SLUG"
|
||||
echo "Using EVE_CHARACTER_ID = $EVE_CHARACTER_ID"
|
||||
echo "-------------------------------------"
|
||||
|
||||
#############################################
|
||||
# 1) Get list of characters (just to confirm they exist)
|
||||
#############################################
|
||||
echo
|
||||
echo "=== 1) Get All Characters (for reference) ==="
|
||||
curl -s "$BASE_URL/api/characters" | jq
|
||||
|
||||
#############################################
|
||||
# 2) Get ACLs for the given map slug
|
||||
#############################################
|
||||
echo
|
||||
echo "=== 2) List ACLs for Map Slug '$MAP_SLUG' ==="
|
||||
ACL_LIST_JSON=$(curl -s -H "Authorization: Bearer $MAP_TOKEN" \
|
||||
"$BASE_URL/api/map/acls?slug=$MAP_SLUG")
|
||||
|
||||
echo "$ACL_LIST_JSON" | jq
|
||||
|
||||
# Attempt to parse out the first ACL ID and token from the JSON data array:
|
||||
FIRST_ACL_ID=$(echo "$ACL_LIST_JSON" | jq -r '.data[0].id // empty')
|
||||
FIRST_ACL_TOKEN=$(echo "$ACL_LIST_JSON" | jq -r '.data[0].api_key // empty')
|
||||
|
||||
#############################################
|
||||
# 3) Decide whether to use an existing ACL or create a new one
|
||||
#############################################
|
||||
if [ -z "$FIRST_ACL_ID" ] || [ "$FIRST_ACL_ID" = "null" ]; then
|
||||
echo "No existing ACL found for map slug: $MAP_SLUG."
|
||||
USE_EXISTING_ACL=false
|
||||
else
|
||||
# We found at least one ACL. But does it have a token?
|
||||
if [ -z "$FIRST_ACL_TOKEN" ] || [ "$FIRST_ACL_TOKEN" = "null" ]; then
|
||||
echo "Found an ACL with ID $FIRST_ACL_ID but no api_key in the response."
|
||||
echo "We cannot do membership actions on it without a token."
|
||||
USE_EXISTING_ACL=false
|
||||
else
|
||||
echo "Parsed ACL_ID from existing ACL: $FIRST_ACL_ID"
|
||||
echo "Parsed ACL_TOKEN from existing ACL: $FIRST_ACL_TOKEN"
|
||||
USE_EXISTING_ACL=true
|
||||
fi
|
||||
fi
|
||||
|
||||
#############################################
|
||||
# 4) If we cannot use an existing ACL, create a new one
|
||||
#############################################
|
||||
if [ "$USE_EXISTING_ACL" = false ]; then
|
||||
echo
|
||||
echo "=== Creating a new ACL for membership testing ==="
|
||||
NEW_ACL_RESPONSE=$(curl -s -X POST \
|
||||
-H "Authorization: Bearer $MAP_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"acl": {
|
||||
"name": "Auto-Created ACL",
|
||||
"description": "Created because none with a token was found",
|
||||
"owner_eve_id": "'"$EVE_CHARACTER_ID"'"
|
||||
}
|
||||
}' \
|
||||
"$BASE_URL/api/map/acls?slug=$MAP_SLUG")
|
||||
|
||||
echo "New ACL creation response:"
|
||||
echo "$NEW_ACL_RESPONSE" | jq
|
||||
|
||||
ACL_ID=$(echo "$NEW_ACL_RESPONSE" | jq -r '.data.id // empty')
|
||||
ACL_TOKEN=$(echo "$NEW_ACL_RESPONSE" | jq -r '.data.api_key // empty')
|
||||
|
||||
if [ -z "$ACL_ID" ] || [ "$ACL_ID" = "null" ] || \
|
||||
[ -z "$ACL_TOKEN" ] || [ "$ACL_TOKEN" = "null" ]; then
|
||||
echo "Failed to create an ACL with a valid token. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Newly created ACL_ID: $ACL_ID"
|
||||
echo "Newly created ACL_TOKEN: $ACL_TOKEN"
|
||||
|
||||
else
|
||||
# Use the existing ACL's details
|
||||
ACL_ID="$FIRST_ACL_ID"
|
||||
ACL_TOKEN="$FIRST_ACL_TOKEN"
|
||||
fi
|
||||
|
||||
#############################################
|
||||
# 5) Show the details of that ACL
|
||||
#############################################
|
||||
echo
|
||||
echo "=== 5) Show ACL Details ==="
|
||||
ACL_DETAILS=$(curl -s \
|
||||
-H "Authorization: Bearer $ACL_TOKEN" \
|
||||
"$BASE_URL/api/acls/$ACL_ID")
|
||||
|
||||
echo "$ACL_DETAILS" | jq || {
|
||||
echo "ACL details response is not valid JSON. Raw response:"
|
||||
echo "$ACL_DETAILS"
|
||||
exit 1
|
||||
}
|
||||
|
||||
#############################################
|
||||
# 6) Create a new ACL member (viewer)
|
||||
#############################################
|
||||
echo
|
||||
echo "=== 6) Create a New ACL Member (viewer) ==="
|
||||
CREATE_MEMBER_RESP=$(curl -s -X POST \
|
||||
-H "Authorization: Bearer $ACL_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"member": {
|
||||
"eve_character_id": "'"$EVE_CHARACTER_ID"'",
|
||||
"role": "viewer"
|
||||
}
|
||||
}' \
|
||||
"$BASE_URL/api/acls/$ACL_ID/members")
|
||||
|
||||
echo "$CREATE_MEMBER_RESP" | jq || {
|
||||
echo "Create member response is not valid JSON. Raw response:"
|
||||
echo "$CREATE_MEMBER_RESP"
|
||||
exit 1
|
||||
}
|
||||
|
||||
#############################################
|
||||
# 7) Update the member's role (e.g., admin)
|
||||
#############################################
|
||||
echo
|
||||
echo "=== 7) Update Member Role to 'admin' ==="
|
||||
UPDATE_MEMBER_RESP=$(curl -s -X PUT \
|
||||
-H "Authorization: Bearer $ACL_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"member": {
|
||||
"role": "admin"
|
||||
}
|
||||
}' \
|
||||
"$BASE_URL/api/acls/$ACL_ID/members/$EVE_CHARACTER_ID")
|
||||
|
||||
echo "$UPDATE_MEMBER_RESP" | jq || {
|
||||
echo "Update member response is not valid JSON. Raw response:"
|
||||
echo "$UPDATE_MEMBER_RESP"
|
||||
exit 1
|
||||
}
|
||||
|
||||
#############################################
|
||||
# 8) Delete the member
|
||||
#############################################
|
||||
echo
|
||||
echo "=== 8) Delete the Member ==="
|
||||
DELETE_MEMBER_RESP=$(curl -s -X DELETE \
|
||||
-H "Authorization: Bearer $ACL_TOKEN" \
|
||||
"$BASE_URL/api/acls/$ACL_ID/members/$EVE_CHARACTER_ID")
|
||||
|
||||
echo "$DELETE_MEMBER_RESP" | jq || {
|
||||
echo "Delete member response is not valid JSON. Raw response:"
|
||||
echo "$DELETE_MEMBER_RESP"
|
||||
exit 1
|
||||
}
|
||||
|
||||
#############################################
|
||||
# 9) (Optional) Update the ACL itself
|
||||
#############################################
|
||||
echo
|
||||
echo "=== 9) Update the ACL’s name/description ==="
|
||||
UPDATED_ACL=$(curl -s -X PUT \
|
||||
-H "Authorization: Bearer $ACL_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"acl": {
|
||||
"name": "Updated ACL Name (script)",
|
||||
"description": "An updated description from test script"
|
||||
}
|
||||
}' \
|
||||
"$BASE_URL/api/acls/$ACL_ID")
|
||||
|
||||
echo "$UPDATED_ACL" | jq || {
|
||||
echo "Update ACL response is not valid JSON. Raw response:"
|
||||
echo "$UPDATED_ACL"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo
|
||||
echo "=== Done! ==="
|
||||
Reference in New Issue
Block a user