mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-12 18:56:01 +00:00
Add api specs (#217)
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user