mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-12 02:35:42 +00:00
539 lines
17 KiB
Elixir
539 lines
17 KiB
Elixir
defmodule WandererAppWeb.OpenApiV1Spec do
|
|
@moduledoc """
|
|
OpenAPI spec specifically for v1 JSON:API endpoints generated by AshJsonApi.
|
|
"""
|
|
|
|
@behaviour OpenApiSpex.OpenApi
|
|
|
|
alias OpenApiSpex.{OpenApi, Info, Server, Components}
|
|
|
|
@impl OpenApiSpex.OpenApi
|
|
def spec do
|
|
# This is called by the modify_open_api option in the router
|
|
# We should return the spec from WandererAppWeb.OpenApi module
|
|
WandererAppWeb.OpenApi.spec()
|
|
end
|
|
|
|
defp generate_spec_manually do
|
|
%OpenApi{
|
|
info: %Info{
|
|
title: "WandererApp v1 JSON:API",
|
|
version: "1.0.0",
|
|
description: """
|
|
JSON:API compliant endpoints for WandererApp.
|
|
|
|
## Features
|
|
- Filtering: Use `filter[attribute]=value` parameters
|
|
- Sorting: Use `sort=attribute` or `sort=-attribute` for descending
|
|
- Pagination: Use `page[limit]=n` and `page[offset]=n`
|
|
- Relationships: Include related resources with `include=relationship`
|
|
|
|
## Authentication
|
|
All endpoints require Bearer token authentication:
|
|
```
|
|
Authorization: Bearer YOUR_API_KEY
|
|
```
|
|
"""
|
|
},
|
|
servers: [
|
|
Server.from_endpoint(WandererAppWeb.Endpoint)
|
|
],
|
|
paths: get_v1_paths(),
|
|
components: %Components{
|
|
schemas: get_v1_schemas(),
|
|
securitySchemes: %{
|
|
"bearerAuth" => %{
|
|
"type" => "http",
|
|
"scheme" => "bearer",
|
|
"description" => "Map API key for authentication"
|
|
}
|
|
}
|
|
},
|
|
security: [%{"bearerAuth" => []}],
|
|
tags: get_v1_tags()
|
|
}
|
|
end
|
|
|
|
defp get_v1_tags do
|
|
[
|
|
%{"name" => "Access Lists", "description" => "Access control list management"},
|
|
%{"name" => "Access List Members", "description" => "ACL member management"},
|
|
%{"name" => "Characters", "description" => "Character management"},
|
|
%{"name" => "Maps", "description" => "Map management"},
|
|
%{"name" => "Map Systems", "description" => "Map system operations"},
|
|
%{"name" => "Map Connections", "description" => "System connection management"},
|
|
%{"name" => "Map Solar Systems", "description" => "Solar system data"},
|
|
%{"name" => "Map System Signatures", "description" => "Wormhole signature tracking"},
|
|
%{"name" => "Map System Structures", "description" => "Structure management"},
|
|
%{"name" => "Map System Comments", "description" => "System comments"},
|
|
%{"name" => "Map Character Settings", "description" => "Character map settings"},
|
|
%{"name" => "Map User Settings", "description" => "User map preferences"},
|
|
%{"name" => "Map Subscriptions", "description" => "Map subscription management"},
|
|
%{"name" => "Map Access Lists", "description" => "Map-specific ACLs"},
|
|
%{"name" => "Map States", "description" => "Map state information"},
|
|
%{"name" => "Users", "description" => "User management"},
|
|
%{"name" => "User Activities", "description" => "User activity tracking"},
|
|
%{"name" => "Ship Type Info", "description" => "Ship type information"}
|
|
]
|
|
end
|
|
|
|
defp get_v1_paths do
|
|
# Generate paths for all resources
|
|
resources = [
|
|
{"access_lists", "Access Lists"},
|
|
{"access_list_members", "Access List Members"},
|
|
{"characters", "Characters"},
|
|
{"maps", "Maps"},
|
|
{"map_systems", "Map Systems"},
|
|
{"map_connections", "Map Connections"},
|
|
{"map_solar_systems", "Map Solar Systems"},
|
|
{"map_system_signatures", "Map System Signatures"},
|
|
{"map_system_structures", "Map System Structures"},
|
|
{"map_system_comments", "Map System Comments"},
|
|
{"map_character_settings", "Map Character Settings"},
|
|
{"map_user_settings", "Map User Settings"},
|
|
{"map_subscriptions", "Map Subscriptions"},
|
|
{"map_access_lists", "Map Access Lists"},
|
|
{"map_states", "Map States"},
|
|
{"users", "Users"},
|
|
{"user_activities", "User Activities"},
|
|
{"ship_type_infos", "Ship Type Info"}
|
|
]
|
|
|
|
Enum.reduce(resources, %{}, fn {resource, tag}, acc ->
|
|
base_path = "/api/v1/#{resource}"
|
|
|
|
paths = %{
|
|
base_path => %{
|
|
"get" => %{
|
|
"summary" => "List #{resource}",
|
|
"tags" => [tag],
|
|
"parameters" => get_standard_list_parameters(resource),
|
|
"responses" => %{
|
|
"200" => %{
|
|
"description" => "List of #{resource}",
|
|
"content" => %{
|
|
"application/vnd.api+json" => %{
|
|
"schema" => %{
|
|
"$ref" => "#/components/schemas/#{String.capitalize(resource)}ListResponse"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"post" => %{
|
|
"summary" => "Create #{String.replace(resource, "_", " ")}",
|
|
"tags" => [tag],
|
|
"requestBody" => %{
|
|
"required" => true,
|
|
"content" => %{
|
|
"application/vnd.api+json" => %{
|
|
"schema" => %{
|
|
"$ref" => "#/components/schemas/#{String.capitalize(resource)}CreateRequest"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses" => %{
|
|
"201" => %{"description" => "Created"}
|
|
}
|
|
}
|
|
},
|
|
"#{base_path}/{id}" => %{
|
|
"get" => %{
|
|
"summary" => "Get #{String.replace(resource, "_", " ")}",
|
|
"tags" => [tag],
|
|
"parameters" => [
|
|
%{
|
|
"name" => "id",
|
|
"in" => "path",
|
|
"required" => true,
|
|
"schema" => %{"type" => "string"}
|
|
}
|
|
],
|
|
"responses" => %{
|
|
"200" => %{"description" => "Resource details"}
|
|
}
|
|
},
|
|
"patch" => %{
|
|
"summary" => "Update #{String.replace(resource, "_", " ")}",
|
|
"tags" => [tag],
|
|
"parameters" => [
|
|
%{
|
|
"name" => "id",
|
|
"in" => "path",
|
|
"required" => true,
|
|
"schema" => %{"type" => "string"}
|
|
}
|
|
],
|
|
"requestBody" => %{
|
|
"required" => true,
|
|
"content" => %{
|
|
"application/vnd.api+json" => %{
|
|
"schema" => %{
|
|
"$ref" => "#/components/schemas/#{String.capitalize(resource)}UpdateRequest"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"responses" => %{
|
|
"200" => %{"description" => "Updated"}
|
|
}
|
|
},
|
|
"delete" => %{
|
|
"summary" => "Delete #{String.replace(resource, "_", " ")}",
|
|
"tags" => [tag],
|
|
"parameters" => [
|
|
%{
|
|
"name" => "id",
|
|
"in" => "path",
|
|
"required" => true,
|
|
"schema" => %{"type" => "string"}
|
|
}
|
|
],
|
|
"responses" => %{
|
|
"204" => %{"description" => "Deleted"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Map.merge(acc, paths)
|
|
end)
|
|
|> add_custom_paths()
|
|
end
|
|
|
|
defp add_custom_paths(paths) do
|
|
# Add custom action paths
|
|
custom_paths = %{
|
|
"/api/v1/maps/{id}/duplicate" => %{
|
|
"post" => %{
|
|
"summary" => "Duplicate map",
|
|
"tags" => ["Maps"],
|
|
"parameters" => [
|
|
%{
|
|
"name" => "id",
|
|
"in" => "path",
|
|
"required" => true,
|
|
"schema" => %{"type" => "string"}
|
|
}
|
|
],
|
|
"responses" => %{
|
|
"201" => %{"description" => "Map duplicated"}
|
|
}
|
|
}
|
|
},
|
|
"/api/v1/maps/{map_id}/systems_and_connections" => %{
|
|
"get" => %{
|
|
"summary" => "Get Map Systems and Connections",
|
|
"description" => "Retrieve both systems and connections for a map in a single response",
|
|
"tags" => ["Maps"],
|
|
"parameters" => [
|
|
%{
|
|
"name" => "map_id",
|
|
"in" => "path",
|
|
"required" => true,
|
|
"schema" => %{"type" => "string"},
|
|
"description" => "Map ID"
|
|
}
|
|
],
|
|
"responses" => %{
|
|
"200" => %{
|
|
"description" => "Combined systems and connections data",
|
|
"content" => %{
|
|
"application/json" => %{
|
|
"schema" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"systems" => %{
|
|
"type" => "array",
|
|
"items" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"id" => %{"type" => "string"},
|
|
"solar_system_id" => %{"type" => "integer"},
|
|
"name" => %{"type" => "string"},
|
|
"status" => %{"type" => "string"},
|
|
"visible" => %{"type" => "boolean"},
|
|
"locked" => %{"type" => "boolean"},
|
|
"position_x" => %{"type" => "integer"},
|
|
"position_y" => %{"type" => "integer"}
|
|
}
|
|
}
|
|
},
|
|
"connections" => %{
|
|
"type" => "array",
|
|
"items" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"id" => %{"type" => "string"},
|
|
"solar_system_source" => %{"type" => "integer"},
|
|
"solar_system_target" => %{"type" => "integer"},
|
|
"type" => %{"type" => "string"},
|
|
"time_status" => %{"type" => "string"},
|
|
"mass_status" => %{"type" => "string"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"404" => %{"description" => "Map not found"},
|
|
"401" => %{"description" => "Unauthorized"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Map.merge(paths, custom_paths)
|
|
end
|
|
|
|
defp get_standard_list_parameters(resource) do
|
|
base_params = [
|
|
%{
|
|
"name" => "sort",
|
|
"in" => "query",
|
|
"description" => "Sort results (e.g., 'name', '-created_at')",
|
|
"schema" => %{"type" => "string"}
|
|
},
|
|
%{
|
|
"name" => "page[limit]",
|
|
"in" => "query",
|
|
"description" => "Number of results per page",
|
|
"schema" => %{"type" => "integer", "default" => 50}
|
|
},
|
|
%{
|
|
"name" => "page[offset]",
|
|
"in" => "query",
|
|
"description" => "Offset for pagination",
|
|
"schema" => %{"type" => "integer", "default" => 0}
|
|
},
|
|
%{
|
|
"name" => "include",
|
|
"in" => "query",
|
|
"description" => "Include related resources (comma-separated)",
|
|
"schema" => %{"type" => "string"}
|
|
}
|
|
]
|
|
|
|
# Add resource-specific filter parameters
|
|
filter_params =
|
|
case resource do
|
|
"characters" ->
|
|
[
|
|
%{
|
|
"name" => "filter[name]",
|
|
"in" => "query",
|
|
"description" => "Filter by character name",
|
|
"schema" => %{"type" => "string"}
|
|
},
|
|
%{
|
|
"name" => "filter[user_id]",
|
|
"in" => "query",
|
|
"description" => "Filter by user ID",
|
|
"schema" => %{"type" => "string"}
|
|
}
|
|
]
|
|
|
|
"maps" ->
|
|
[
|
|
%{
|
|
"name" => "filter[scope]",
|
|
"in" => "query",
|
|
"description" => "Filter by map scope",
|
|
"schema" => %{"type" => "string"}
|
|
},
|
|
%{
|
|
"name" => "filter[archived]",
|
|
"in" => "query",
|
|
"description" => "Filter by archived status",
|
|
"schema" => %{"type" => "boolean"}
|
|
}
|
|
]
|
|
|
|
"map_systems" ->
|
|
[
|
|
%{
|
|
"name" => "filter[map_id]",
|
|
"in" => "query",
|
|
"description" => "Filter by map ID",
|
|
"schema" => %{"type" => "string"}
|
|
},
|
|
%{
|
|
"name" => "filter[solar_system_id]",
|
|
"in" => "query",
|
|
"description" => "Filter by solar system ID",
|
|
"schema" => %{"type" => "integer"}
|
|
}
|
|
]
|
|
|
|
"map_connections" ->
|
|
[
|
|
%{
|
|
"name" => "filter[map_id]",
|
|
"in" => "query",
|
|
"description" => "Filter by map ID",
|
|
"schema" => %{"type" => "string"}
|
|
},
|
|
%{
|
|
"name" => "filter[source_id]",
|
|
"in" => "query",
|
|
"description" => "Filter by source system ID",
|
|
"schema" => %{"type" => "string"}
|
|
},
|
|
%{
|
|
"name" => "filter[target_id]",
|
|
"in" => "query",
|
|
"description" => "Filter by target system ID",
|
|
"schema" => %{"type" => "string"}
|
|
}
|
|
]
|
|
|
|
"map_system_signatures" ->
|
|
[
|
|
%{
|
|
"name" => "filter[system_id]",
|
|
"in" => "query",
|
|
"description" => "Filter by system ID",
|
|
"schema" => %{"type" => "string"}
|
|
},
|
|
%{
|
|
"name" => "filter[type]",
|
|
"in" => "query",
|
|
"description" => "Filter by signature type",
|
|
"schema" => %{"type" => "string"}
|
|
}
|
|
]
|
|
|
|
_ ->
|
|
[]
|
|
end
|
|
|
|
base_params ++ filter_params
|
|
end
|
|
|
|
defp get_v1_schemas do
|
|
%{
|
|
# Generic JSON:API response wrapper
|
|
"JsonApiWrapper" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"data" => %{
|
|
"type" => "object",
|
|
"description" => "Primary data"
|
|
},
|
|
"included" => %{
|
|
"type" => "array",
|
|
"description" => "Included related resources"
|
|
},
|
|
"meta" => %{
|
|
"type" => "object",
|
|
"description" => "Metadata about the response"
|
|
},
|
|
"links" => %{
|
|
"type" => "object",
|
|
"description" => "Links for pagination and relationships"
|
|
}
|
|
}
|
|
},
|
|
# Character schemas
|
|
"CharacterResource" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"type" => %{"type" => "string", "enum" => ["characters"]},
|
|
"id" => %{"type" => "string"},
|
|
"attributes" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"name" => %{"type" => "string"},
|
|
"eve_id" => %{"type" => "integer"},
|
|
"corporation_id" => %{"type" => "integer"},
|
|
"alliance_id" => %{"type" => "integer"},
|
|
"online" => %{"type" => "boolean"},
|
|
"location" => %{"type" => "object"},
|
|
"inserted_at" => %{"type" => "string", "format" => "date-time"},
|
|
"updated_at" => %{"type" => "string", "format" => "date-time"}
|
|
}
|
|
},
|
|
"relationships" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"user" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"data" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"type" => %{"type" => "string"},
|
|
"id" => %{"type" => "string"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"CharactersListResponse" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"data" => %{
|
|
"type" => "array",
|
|
"items" => %{"$ref" => "#/components/schemas/CharacterResource"}
|
|
},
|
|
"meta" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"page" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"offset" => %{"type" => "integer"},
|
|
"limit" => %{"type" => "integer"},
|
|
"total" => %{"type" => "integer"}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
# Map schemas
|
|
"MapResource" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"type" => %{"type" => "string", "enum" => ["maps"]},
|
|
"id" => %{"type" => "string"},
|
|
"attributes" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"name" => %{"type" => "string"},
|
|
"slug" => %{"type" => "string"},
|
|
"scope" => %{"type" => "string"},
|
|
"public_key" => %{"type" => "string"},
|
|
"archived" => %{"type" => "boolean"},
|
|
"inserted_at" => %{"type" => "string", "format" => "date-time"},
|
|
"updated_at" => %{"type" => "string", "format" => "date-time"}
|
|
}
|
|
},
|
|
"relationships" => %{
|
|
"type" => "object",
|
|
"properties" => %{
|
|
"owner" => %{
|
|
"type" => "object"
|
|
},
|
|
"characters" => %{
|
|
"type" => "object"
|
|
},
|
|
"acls" => %{
|
|
"type" => "object"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
end
|
|
end
|