Compare commits

..

12 Commits

Author SHA1 Message Date
CI
8a9807d3e5 chore: release version v1.77.3 2025-08-29 00:33:30 +00:00
Dmitry Popov
39df3c97ce Merge pull request #505 from wanderer-industries/tracking-fix
Tracking fix
2025-08-29 04:33:00 +04:00
Dmitry Popov
46c1ccdfcc fix: Fixed character tracking settings 2025-08-29 02:31:00 +02:00
Dmitry Popov
8817536038 fix: Fixed character tracking settings 2025-08-29 02:30:19 +02:00
Dmitry Popov
c3bb23a6ee fix: Fixed character tracking settings 2025-08-29 01:41:08 +02:00
Dmitry Popov
7e9c4c575e fix: Fixed character tracking settings 2025-08-28 22:18:18 +02:00
CI
5a70eee91e chore: [skip ci] 2025-08-28 10:24:51 +00:00
CI
228f6990a1 chore: release version v1.77.2 2025-08-28 10:24:51 +00:00
Dmitry Popov
d80ed0e70e Merge pull request #504 from guarzo/guarzo/sigapi
fix: update system signature api to return correct system id
2025-08-28 14:24:26 +04:00
CI
4576c75737 chore: [skip ci] 2025-08-28 10:03:36 +00:00
guarzo
99dcf49fbc Merge branch 'main' into guarzo/sigapi 2025-08-27 21:30:59 -04:00
guarzo
6fb3edbfd6 fix: update system signature api to return correct system id 2025-08-28 01:30:37 +00:00
21 changed files with 357 additions and 275 deletions

View File

@@ -2,6 +2,30 @@
<!-- changelog --> <!-- changelog -->
## [v1.77.3](https://github.com/wanderer-industries/wanderer/compare/v1.77.2...v1.77.3) (2025-08-29)
### Bug Fixes:
* Fixed character tracking settings
* Fixed character tracking settings
* Fixed character tracking settings
* Fixed character tracking settings
## [v1.77.2](https://github.com/wanderer-industries/wanderer/compare/v1.77.1...v1.77.2) (2025-08-28)
### Bug Fixes:
* update system signature api to return correct system id
## [v1.77.1](https://github.com/wanderer-industries/wanderer/compare/v1.77.0...v1.77.1) (2025-08-28) ## [v1.77.1](https://github.com/wanderer-industries/wanderer/compare/v1.77.0...v1.77.1) (2025-08-28)

View File

@@ -79,8 +79,7 @@ defmodule WandererApp.Api.MapCharacterSettings do
accept [ accept [
:map_id, :map_id,
:character_id, :character_id,
:tracked, :tracked
:followed
] ]
argument :map_id, :uuid, allow_nil?: false argument :map_id, :uuid, allow_nil?: false

View File

@@ -129,21 +129,23 @@ defmodule WandererApp.CachedInfo do
def get_solar_system_jump(from_solar_system_id, to_solar_system_id) do def get_solar_system_jump(from_solar_system_id, to_solar_system_id) do
# Create normalized cache key (smaller ID first for bidirectional lookup) # Create normalized cache key (smaller ID first for bidirectional lookup)
{id1, id2} = if from_solar_system_id < to_solar_system_id do {id1, id2} =
{from_solar_system_id, to_solar_system_id} if from_solar_system_id < to_solar_system_id do
else {from_solar_system_id, to_solar_system_id}
{to_solar_system_id, from_solar_system_id} else
end {to_solar_system_id, from_solar_system_id}
end
cache_key = "jump_#{id1}_#{id2}" cache_key = "jump_#{id1}_#{id2}"
case WandererApp.Cache.lookup(cache_key) do case WandererApp.Cache.lookup(cache_key) do
{:ok, nil} -> {:ok, nil} ->
# Build jump index if not exists # Build jump index if not exists
build_jump_index() build_jump_index()
WandererApp.Cache.lookup(cache_key) WandererApp.Cache.lookup(cache_key)
result -> result result ->
result
end end
end end
@@ -152,17 +154,19 @@ defmodule WandererApp.CachedInfo do
{:ok, jumps} -> {:ok, jumps} ->
jumps jumps
|> Enum.each(fn jump -> |> Enum.each(fn jump ->
{id1, id2} = if jump.from_solar_system_id < jump.to_solar_system_id do {id1, id2} =
{jump.from_solar_system_id, jump.to_solar_system_id} if jump.from_solar_system_id < jump.to_solar_system_id do
else {jump.from_solar_system_id, jump.to_solar_system_id}
{jump.to_solar_system_id, jump.from_solar_system_id} else
end {jump.to_solar_system_id, jump.from_solar_system_id}
end
cache_key = "jump_#{id1}_#{id2}" cache_key = "jump_#{id1}_#{id2}"
WandererApp.Cache.put(cache_key, jump) WandererApp.Cache.put(cache_key, jump)
end) end)
_ -> :error _ ->
:error
end end
end end

View File

@@ -20,7 +20,7 @@ defmodule WandererApp.Character.TrackingUtils do
) )
when not is_nil(caller_pid) do when not is_nil(caller_pid) do
with {:ok, character} <- with {:ok, character} <-
WandererApp.Character.get_by_eve_id(character_eve_id), WandererApp.Character.get_by_eve_id("#{character_eve_id}"),
{:ok, %{tracked: is_tracked}} <- {:ok, %{tracked: is_tracked}} <-
do_update_character_tracking(character, map_id, track, caller_pid) do do_update_character_tracking(character, map_id, track, caller_pid) do
# Determine which event to send based on tracking mode and previous state # Determine which event to send based on tracking mode and previous state
@@ -55,15 +55,19 @@ defmodule WandererApp.Character.TrackingUtils do
Builds tracking data for all characters with access to a map. Builds tracking data for all characters with access to a map.
""" """
def build_tracking_data(map_id, current_user_id) do def build_tracking_data(map_id, current_user_id) do
with {:ok, map} <- WandererApp.MapRepo.get(map_id, [:acls]), with {:ok, map} <-
{:ok, character_settings} <- WandererApp.MapRepo.get(map_id,
WandererApp.Character.Activity.get_map_character_settings(map_id), acls: [
:owner_id,
members: [:role, :eve_character_id, :eve_corporation_id, :eve_alliance_id]
]
),
{:ok, user_settings} <- WandererApp.MapUserSettingsRepo.get(map_id, current_user_id), {:ok, user_settings} <- WandererApp.MapUserSettingsRepo.get(map_id, current_user_id),
{:ok, %{characters: characters_with_access}} <- {:ok, %{characters: characters_with_access}} <-
WandererApp.Maps.load_characters(map, character_settings, current_user_id) do WandererApp.Maps.load_characters(map, current_user_id) do
# Map characters to tracking data # Map characters to tracking data
{:ok, characters_data} = {:ok, characters_data} =
build_character_tracking_data(characters_with_access, character_settings) build_character_tracking_data(characters_with_access)
{:ok, main_character} = {:ok, main_character} =
get_main_character(user_settings, characters_with_access, characters_with_access) get_main_character(user_settings, characters_with_access, characters_with_access)
@@ -98,21 +102,19 @@ defmodule WandererApp.Character.TrackingUtils do
end end
# Helper to build tracking data for each character # Helper to build tracking data for each character
defp build_character_tracking_data(characters, character_settings) do defp build_character_tracking_data(characters) do
{:ok, {:ok,
Enum.map(characters, fn char -> Enum.map(characters, fn char ->
setting = Enum.find(character_settings, &(&1.character_id == char.id))
%{ %{
character: char |> WandererAppWeb.MapEventHandler.map_ui_character_stat(), character: char |> WandererAppWeb.MapEventHandler.map_ui_character_stat(),
tracked: (setting && setting.tracked) || false tracked: char.tracked
} }
end)} end)}
end end
# Private implementation of update character tracking # Private implementation of update character tracking
defp do_update_character_tracking(character, map_id, track, caller_pid) do defp do_update_character_tracking(character, map_id, track, caller_pid) do
WandererApp.MapCharacterSettingsRepo.get_by_map(map_id, character.id) WandererApp.MapCharacterSettingsRepo.get(map_id, character.id)
|> case do |> case do
# Untracking flow # Untracking flow
{:ok, %{tracked: true} = existing_settings} -> {:ok, %{tracked: true} = existing_settings} ->

View File

@@ -88,11 +88,18 @@ defmodule WandererApp.Map.Server do
|> map_pid! |> map_pid!
|> GenServer.cast({&Impl.remove_character/2, [character_id]}) |> GenServer.cast({&Impl.remove_character/2, [character_id]})
def untrack_characters(map_id, character_ids) when is_binary(map_id), def untrack_characters(map_id, character_ids) when is_binary(map_id) do
do: map_id
map_id |> map_pid()
|> map_pid! |> case do
|> GenServer.cast({&Impl.untrack_characters/2, [character_ids]}) pid when is_pid(pid) ->
GenServer.cast(pid, {&Impl.untrack_characters/2, [character_ids]})
_ ->
WandererApp.Cache.insert("map_#{map_id}:started", false)
:ok
end
end
def add_system(map_id, system_info, user_id, character_id) when is_binary(map_id), def add_system(map_id, system_info, user_id, character_id) when is_binary(map_id),
do: do:

View File

@@ -16,7 +16,13 @@ defmodule WandererApp.Map.Operations.Signatures do
systems systems
|> Enum.flat_map(fn sys -> |> Enum.flat_map(fn sys ->
with {:ok, sigs} <- MapSystemSignature.by_system_id(sys.id) do with {:ok, sigs} <- MapSystemSignature.by_system_id(sys.id) do
sigs # Add solar_system_id to each signature and remove system_id
Enum.map(sigs, fn sig ->
sig
|> Map.from_struct()
|> Map.put(:solar_system_id, sys.solar_system_id)
|> Map.drop([:system_id, :__meta__, :system, :aggregates, :calculations])
end)
else else
err -> err ->
Logger.error("[list_signatures] error: #{inspect(err)}") Logger.error("[list_signatures] error: #{inspect(err)}")
@@ -32,28 +38,70 @@ defmodule WandererApp.Map.Operations.Signatures do
def create_signature( def create_signature(
%{assigns: %{map_id: map_id, owner_character_id: char_id, owner_user_id: user_id}} = %{assigns: %{map_id: map_id, owner_character_id: char_id, owner_user_id: user_id}} =
_conn, _conn,
%{"solar_system_id" => _solar_system_id} = params %{"solar_system_id" => solar_system_id} = params
) do )
attrs = Map.put(params, "character_eve_id", char_id) when is_integer(solar_system_id) do
# Convert solar_system_id to system_id for internal use
with {:ok, system} <- MapSystem.by_map_id_and_solar_system_id(map_id, solar_system_id) do
attrs =
params
|> Map.put("character_eve_id", char_id)
|> Map.put("system_id", system.id)
|> Map.delete("solar_system_id")
case Server.update_signatures(map_id, %{ case Server.update_signatures(map_id, %{
added_signatures: [attrs], added_signatures: [attrs],
updated_signatures: [], updated_signatures: [],
removed_signatures: [], removed_signatures: [],
solar_system_id: params["solar_system_id"], solar_system_id: solar_system_id,
character_id: char_id, character_id: char_id,
user_id: user_id, user_id: user_id,
delete_connection_with_sigs: false delete_connection_with_sigs: false
}) do }) do
:ok -> :ok ->
{:ok, attrs} # Try to fetch the created signature to return with proper fields
with {:ok, sigs} <-
MapSystemSignature.by_system_id_and_eve_ids(system.id, [attrs["eve_id"]]),
sig when not is_nil(sig) <- List.first(sigs) do
result =
sig
|> Map.from_struct()
|> Map.put(:solar_system_id, system.solar_system_id)
|> Map.drop([:system_id, :__meta__, :system, :aggregates, :calculations])
err -> {:ok, result}
Logger.error("[create_signature] Unexpected error: #{inspect(err)}") else
{:error, :unexpected_error} _ ->
# Fallback: return attrs with solar_system_id added
attrs_result =
attrs
|> Map.put(:solar_system_id, solar_system_id)
|> Map.drop(["system_id"])
{:ok, attrs_result}
end
err ->
Logger.error("[create_signature] Unexpected error: #{inspect(err)}")
{:error, :unexpected_error}
end
else
_ ->
Logger.error(
"[create_signature] System not found for solar_system_id: #{solar_system_id}"
)
{:error, :system_not_found}
end end
end end
def create_signature(
%{assigns: %{map_id: _map_id, owner_character_id: _char_id, owner_user_id: _user_id}} =
_conn,
%{"solar_system_id" => _invalid} = _params
),
do: {:error, :missing_params}
def create_signature(_conn, _params), do: {:error, :missing_params} def create_signature(_conn, _params), do: {:error, :missing_params}
@spec update_signature(Plug.Conn.t(), String.t(), map()) :: {:ok, map()} | {:error, atom()} @spec update_signature(Plug.Conn.t(), String.t(), map()) :: {:ok, map()} | {:error, atom()}
@@ -90,7 +138,18 @@ defmodule WandererApp.Map.Operations.Signatures do
delete_connection_with_sigs: false delete_connection_with_sigs: false
}) })
{:ok, attrs} # Fetch the updated signature to return with proper fields
with {:ok, updated_sig} <- MapSystemSignature.by_id(sig_id) do
result =
updated_sig
|> Map.from_struct()
|> Map.put(:solar_system_id, system.solar_system_id)
|> Map.drop([:system_id, :__meta__, :system, :aggregates, :calculations])
{:ok, result}
else
_ -> {:ok, attrs}
end
else else
err -> err ->
Logger.error("[update_signature] Unexpected error: #{inspect(err)}") Logger.error("[update_signature] Unexpected error: #{inspect(err)}")

View File

@@ -59,6 +59,7 @@ defmodule WandererApp.Map.Server.AclsImpl do
map_update = %{acls: map.acls, scope: map.scope} map_update = %{acls: map.acls, scope: map.scope}
WandererApp.Map.update_map(map_id, map_update) WandererApp.Map.update_map(map_id, map_update)
WandererApp.Cache.delete("map_characters-#{map_id}")
broadcast_acl_updates({:ok, result}, map_id) broadcast_acl_updates({:ok, result}, map_id)
@@ -66,7 +67,7 @@ defmodule WandererApp.Map.Server.AclsImpl do
end end
def handle_acl_updated(map_id, acl_id) do def handle_acl_updated(map_id, acl_id) do
{:ok, map} = {:ok, %{acls: acls}} =
WandererApp.MapRepo.get(map_id, WandererApp.MapRepo.get(map_id,
acls: [ acls: [
:owner_id, :owner_id,
@@ -74,8 +75,9 @@ defmodule WandererApp.Map.Server.AclsImpl do
] ]
) )
if map.acls |> Enum.map(& &1.id) |> Enum.member?(acl_id) do if acls |> Enum.map(& &1.id) |> Enum.member?(acl_id) do
WandererApp.Map.update_map(map_id, %{acls: map.acls}) WandererApp.Map.update_map(map_id, %{acls: acls})
WandererApp.Cache.delete("map_characters-#{map_id}")
:ok = :ok =
acl_id acl_id
@@ -85,7 +87,7 @@ defmodule WandererApp.Map.Server.AclsImpl do
end end
def handle_acl_deleted(map_id, _acl_id) do def handle_acl_deleted(map_id, _acl_id) do
{:ok, map} = {:ok, %{acls: acls}} =
WandererApp.MapRepo.get(map_id, WandererApp.MapRepo.get(map_id,
acls: [ acls: [
:owner_id, :owner_id,
@@ -93,7 +95,8 @@ defmodule WandererApp.Map.Server.AclsImpl do
] ]
) )
WandererApp.Map.update_map(map_id, %{acls: map.acls}) WandererApp.Map.update_map(map_id, %{acls: acls})
WandererApp.Cache.delete("map_characters-#{map_id}")
character_ids = character_ids =
map_id map_id

View File

@@ -78,15 +78,12 @@ defmodule WandererApp.Map.Server.CharactersImpl do
characters_to_remove = old_map_tracked_characters -- map_active_tracked_characters characters_to_remove = old_map_tracked_characters -- map_active_tracked_characters
{:ok, invalidate_character_ids} = WandererApp.Cache.insert_or_update(
WandererApp.Cache.lookup(
"map_#{map_id}:invalidate_character_ids",
[]
)
WandererApp.Cache.insert(
"map_#{map_id}:invalidate_character_ids", "map_#{map_id}:invalidate_character_ids",
(invalidate_character_ids ++ characters_to_remove) |> Enum.uniq() characters_to_remove,
fn ids ->
(ids ++ characters_to_remove) |> Enum.uniq()
end
) )
WandererApp.Cache.insert("maps:#{map_id}:tracked_characters", map_active_tracked_characters) WandererApp.Cache.insert("maps:#{map_id}:tracked_characters", map_active_tracked_characters)
@@ -126,15 +123,18 @@ defmodule WandererApp.Map.Server.CharactersImpl do
def cleanup_characters(map_id, owner_id) do def cleanup_characters(map_id, owner_id) do
{:ok, invalidate_character_ids} = {:ok, invalidate_character_ids} =
WandererApp.Cache.lookup( WandererApp.Cache.get_and_remove(
"map_#{map_id}:invalidate_character_ids", "map_#{map_id}:invalidate_character_ids",
[] []
) )
acls = {:ok, %{acls: acls}} =
map_id WandererApp.MapRepo.get(map_id,
|> WandererApp.Map.get_map!() acls: [
|> Map.get(:acls, []) :owner_id,
members: [:role, :eve_character_id, :eve_corporation_id, :eve_alliance_id]
]
)
invalidate_character_ids invalidate_character_ids
|> Task.async_stream( |> Task.async_stream(
@@ -186,11 +186,6 @@ defmodule WandererApp.Map.Server.CharactersImpl do
{:error, reason} -> {:error, reason} ->
Logger.error("Error in cleanup_characters: #{inspect(reason)}") Logger.error("Error in cleanup_characters: #{inspect(reason)}")
end) end)
WandererApp.Cache.insert(
"map_#{map_id}:invalidate_character_ids",
[]
)
end end
defp remove_and_untrack_characters(map_id, character_ids) do defp remove_and_untrack_characters(map_id, character_ids) do

View File

@@ -25,7 +25,7 @@ defmodule WandererApp.Map.Server.Impl do
] ]
@systems_cleanup_timeout :timer.minutes(30) @systems_cleanup_timeout :timer.minutes(30)
@characters_cleanup_timeout :timer.minutes(1) @characters_cleanup_timeout :timer.minutes(5)
@connections_cleanup_timeout :timer.minutes(2) @connections_cleanup_timeout :timer.minutes(2)
@pubsub_client Application.compile_env(:wanderer_app, :pubsub_client) @pubsub_client Application.compile_env(:wanderer_app, :pubsub_client)
@@ -100,7 +100,7 @@ defmodule WandererApp.Map.Server.Impl do
Process.send_after(self(), :update_presence, @update_presence_timeout) Process.send_after(self(), :update_presence, @update_presence_timeout)
Process.send_after(self(), :cleanup_connections, 5_000) Process.send_after(self(), :cleanup_connections, 5_000)
Process.send_after(self(), :cleanup_systems, 10_000) Process.send_after(self(), :cleanup_systems, 10_000)
Process.send_after(self(), :cleanup_characters, :timer.minutes(5)) Process.send_after(self(), :cleanup_characters, @characters_cleanup_timeout)
Process.send_after(self(), :backup_state, @backup_state_timeout) Process.send_after(self(), :backup_state, @backup_state_timeout)
WandererApp.Cache.insert("map_#{map_id}:started", true) WandererApp.Cache.insert("map_#{map_id}:started", true)
@@ -127,6 +127,7 @@ defmodule WandererApp.Map.Server.Impl do
Logger.debug(fn -> "Stopping map server for #{map_id}" end) Logger.debug(fn -> "Stopping map server for #{map_id}" end)
WandererApp.Cache.delete("map_#{map_id}:started") WandererApp.Cache.delete("map_#{map_id}:started")
WandererApp.Cache.delete("map_characters-#{map_id}")
:telemetry.execute([:wanderer_app, :map, :stopped], %{count: 1}) :telemetry.execute([:wanderer_app, :map, :stopped], %{count: 1})
@@ -278,7 +279,7 @@ defmodule WandererApp.Map.Server.Impl do
end end
def handle_event({:acl_deleted, %{acl_id: acl_id}}, %{map_id: map_id} = state) do def handle_event({:acl_deleted, %{acl_id: acl_id}}, %{map_id: map_id} = state) do
AclsImpl.handle_acl_updated(map_id, acl_id) AclsImpl.handle_acl_deleted(map_id, acl_id)
state state
end end

View File

@@ -94,13 +94,22 @@ defmodule WandererApp.Maps do
end end
end end
def load_characters(map, character_settings, user_id) do def load_characters(map, user_id) do
{:ok, user_characters} = {:ok, user_characters} =
WandererApp.Api.Character.active_by_user(%{user_id: user_id}) WandererApp.Api.Character.active_by_user(%{user_id: user_id})
characters = map_available_characters =
map map
|> get_map_available_characters(user_characters) |> get_map_available_characters(user_characters)
{:ok, character_settings} =
WandererApp.MapCharacterSettingsRepo.get_by_map_filtered(
map.id,
map_available_characters |> Enum.map(& &1.id)
)
characters =
map_available_characters
|> Enum.map(fn c -> |> Enum.map(fn c ->
map_character(c, character_settings |> Enum.find(&(&1.character_id == c.id))) map_character(c, character_settings |> Enum.find(&(&1.character_id == c.id)))
end) end)
@@ -176,48 +185,57 @@ defmodule WandererApp.Maps do
tracked: tracked tracked: tracked
} }
@decorate cacheable( defp get_map_characters(%{id: map_id} = map) do
cache: WandererApp.Cache, WandererApp.Cache.lookup!("map_characters-#{map_id}")
key: "map_characters-#{map_id}", |> case do
opts: [ttl: :timer.seconds(2)] nil ->
) map_acls =
defp _get_map_characters(%{id: map_id} = map) do map.acls
map_acls = |> Enum.map(fn acl -> acl |> Ash.load!(:members) end)
map.acls
|> Enum.map(fn acl -> acl |> Ash.load!(:members) end)
map_acl_owner_ids = map_acl_owner_ids =
map_acls map_acls
|> Enum.map(fn acl -> acl.owner_id end) |> Enum.map(fn acl -> acl.owner_id end)
map_members = map_members =
map_acls map_acls
|> Enum.map(fn acl -> acl.members end) |> Enum.map(fn acl -> acl.members end)
|> List.flatten() |> List.flatten()
|> Enum.filter(fn member -> member.role != :blocked end) |> Enum.filter(fn member -> member.role != :blocked end)
map_member_eve_ids = map_member_eve_ids =
map_members map_members
|> Enum.filter(fn member -> not is_nil(member.eve_character_id) end) |> Enum.filter(fn member -> not is_nil(member.eve_character_id) end)
|> Enum.map(fn member -> member.eve_character_id end) |> Enum.map(fn member -> member.eve_character_id end)
map_member_corporation_ids = map_member_corporation_ids =
map_members map_members
|> Enum.filter(fn member -> not is_nil(member.eve_corporation_id) end) |> Enum.filter(fn member -> not is_nil(member.eve_corporation_id) end)
|> Enum.map(fn member -> member.eve_corporation_id end) |> Enum.map(fn member -> member.eve_corporation_id end)
map_member_alliance_ids = map_member_alliance_ids =
map_members map_members
|> Enum.filter(fn member -> not is_nil(member.eve_alliance_id) end) |> Enum.filter(fn member -> not is_nil(member.eve_alliance_id) end)
|> Enum.map(fn member -> member.eve_alliance_id end) |> Enum.map(fn member -> member.eve_alliance_id end)
{:ok, map_characters =
%{ %{
map_acl_owner_ids: map_acl_owner_ids, map_acl_owner_ids: map_acl_owner_ids,
map_member_eve_ids: map_member_eve_ids, map_member_eve_ids: map_member_eve_ids,
map_member_corporation_ids: map_member_corporation_ids, map_member_corporation_ids: map_member_corporation_ids,
map_member_alliance_ids: map_member_alliance_ids map_member_alliance_ids: map_member_alliance_ids
}} }
WandererApp.Cache.insert(
"map_characters-#{map_id}",
map_characters
)
{:ok, map_characters}
map_characters ->
{:ok, map_characters}
end
end end
defp get_map_available_characters(map, user_characters) do defp get_map_available_characters(map, user_characters) do
@@ -227,7 +245,7 @@ defmodule WandererApp.Maps do
map_member_eve_ids: map_member_eve_ids, map_member_eve_ids: map_member_eve_ids,
map_member_corporation_ids: map_member_corporation_ids, map_member_corporation_ids: map_member_corporation_ids,
map_member_alliance_ids: map_member_alliance_ids map_member_alliance_ids: map_member_alliance_ids
}} = _get_map_characters(map) }} = get_map_characters(map)
user_characters user_characters
|> Enum.filter(fn c -> |> Enum.filter(fn c ->

View File

@@ -53,20 +53,8 @@ defmodule WandererApp.MapCharacterSettingsRepo do
def get_tracked_by_map_all(map_id), def get_tracked_by_map_all(map_id),
do: WandererApp.Api.MapCharacterSettings.tracked_by_map_all(%{map_id: map_id}) do: WandererApp.Api.MapCharacterSettings.tracked_by_map_all(%{map_id: map_id})
def get_by_map(map_id, character_id) do
case get_by_map_filtered(map_id, [character_id]) do
{:ok, [setting | _]} ->
{:ok, setting}
{:ok, []} ->
{:error, :not_found}
{:error, reason} ->
{:error, reason}
end
end
def track(settings) do def track(settings) do
{:ok, _} = get(settings.map_id, settings.character_id)
# Only update the tracked field, preserving other fields # Only update the tracked field, preserving other fields
WandererApp.Api.MapCharacterSettings.track(%{ WandererApp.Api.MapCharacterSettings.track(%{
map_id: settings.map_id, map_id: settings.map_id,
@@ -75,6 +63,7 @@ defmodule WandererApp.MapCharacterSettingsRepo do
end end
def untrack(settings) do def untrack(settings) do
{:ok, _} = get(settings.map_id, settings.character_id)
# Only update the tracked field, preserving other fields # Only update the tracked field, preserving other fields
WandererApp.Api.MapCharacterSettings.untrack(%{ WandererApp.Api.MapCharacterSettings.untrack(%{
map_id: settings.map_id, map_id: settings.map_id,
@@ -83,22 +72,16 @@ defmodule WandererApp.MapCharacterSettingsRepo do
end end
def track!(settings) do def track!(settings) do
case WandererApp.Api.MapCharacterSettings.track(%{ case track(settings) do
map_id: settings.map_id,
character_id: settings.character_id
}) do
{:ok, result} -> result {:ok, result} -> result
{:error, error} -> raise "Failed to track: #{inspect(error)}" error -> raise "Failed to track: #{inspect(error)}"
end end
end end
def untrack!(settings) do def untrack!(settings) do
case WandererApp.Api.MapCharacterSettings.untrack(%{ case untrack(settings) do
map_id: settings.map_id,
character_id: settings.character_id
}) do
{:ok, result} -> result {:ok, result} -> result
{:error, error} -> raise "Failed to untrack: #{inspect(error)}" error -> raise "Failed to untrack: #{inspect(error)}"
end end
end end
@@ -117,22 +100,16 @@ defmodule WandererApp.MapCharacterSettingsRepo do
end end
def follow!(settings) do def follow!(settings) do
case WandererApp.Api.MapCharacterSettings.follow(%{ case follow(settings) do
map_id: settings.map_id,
character_id: settings.character_id
}) do
{:ok, result} -> result {:ok, result} -> result
{:error, error} -> raise "Failed to follow: #{inspect(error)}" error -> raise "Failed to follow: #{inspect(error)}"
end end
end end
def unfollow!(settings) do def unfollow!(settings) do
case WandererApp.Api.MapCharacterSettings.unfollow(%{ case unfollow(settings) do
map_id: settings.map_id,
character_id: settings.character_id
}) do
{:ok, result} -> result {:ok, result} -> result
{:error, error} -> raise "Failed to unfollow: #{inspect(error)}" error -> raise "Failed to unfollow: #{inspect(error)}"
end end
end end

View File

@@ -15,7 +15,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIController do
type: :object, type: :object,
properties: %{ properties: %{
id: %OpenApiSpex.Schema{type: :string, format: :uuid}, id: %OpenApiSpex.Schema{type: :string, format: :uuid},
system_id: %OpenApiSpex.Schema{type: :string, format: :uuid}, solar_system_id: %OpenApiSpex.Schema{type: :integer},
eve_id: %OpenApiSpex.Schema{type: :string}, eve_id: %OpenApiSpex.Schema{type: :string},
character_eve_id: %OpenApiSpex.Schema{type: :string}, character_eve_id: %OpenApiSpex.Schema{type: :string},
name: %OpenApiSpex.Schema{type: :string, nullable: true}, name: %OpenApiSpex.Schema{type: :string, nullable: true},
@@ -31,13 +31,13 @@ defmodule WandererAppWeb.MapSystemSignatureAPIController do
}, },
required: [ required: [
:id, :id,
:system_id, :solar_system_id,
:eve_id, :eve_id,
:character_eve_id :character_eve_id
], ],
example: %{ example: %{
id: "sig-uuid-1", id: "sig-uuid-1",
system_id: "sys-uuid-1", solar_system_id: 30_000_142,
eve_id: "ABC-123", eve_id: "ABC-123",
character_eve_id: "123456789", character_eve_id: "123456789",
name: "Wormhole K162", name: "Wormhole K162",
@@ -122,7 +122,15 @@ defmodule WandererAppWeb.MapSystemSignatureAPIController do
{:ok, signature} -> {:ok, signature} ->
case WandererApp.Api.MapSystem.by_id(signature.system_id) do case WandererApp.Api.MapSystem.by_id(signature.system_id) do
{:ok, system} when system.map_id == map_id -> {:ok, system} when system.map_id == map_id ->
json(conn, %{data: signature}) # Add solar_system_id and remove system_id
# Convert to a plain map to avoid encoder issues
signature_data =
signature
|> Map.from_struct()
|> Map.put(:solar_system_id, system.solar_system_id)
|> Map.drop([:system_id, :__meta__, :system, :aggregates, :calculations])
json(conn, %{data: signature_data})
_ -> _ ->
conn |> put_status(:not_found) |> json(%{error: "Signature not found"}) conn |> put_status(:not_found) |> json(%{error: "Signature not found"})

View File

@@ -13,6 +13,7 @@ defmodule WandererAppWeb.Plugs.CheckJsonApiAuth do
import Plug.Conn import Plug.Conn
alias Plug.Crypto
alias WandererApp.Api.User alias WandererApp.Api.User
alias WandererApp.SecurityAudit alias WandererApp.SecurityAudit
alias WandererApp.Audit.RequestContext alias WandererApp.Audit.RequestContext
@@ -140,43 +141,60 @@ defmodule WandererAppWeb.Plugs.CheckJsonApiAuth do
defp authenticate_bearer_token(conn) do defp authenticate_bearer_token(conn) do
case get_req_header(conn, "authorization") do case get_req_header(conn, "authorization") do
["Bearer " <> token] -> ["Bearer " <> token] ->
validate_api_token(token) validate_api_token(conn, token)
_ -> _ ->
{:error, "Missing or invalid authorization header"} {:error, "Missing or invalid authorization header"}
end end
end end
defp validate_api_token(token) do defp validate_api_token(conn, token) do
# Look up the map by its public API key # Check for map identifier in path params
case find_map_by_api_key(token) do # According to PR feedback, routes supply params["map_identifier"]
{:ok, map} when not is_nil(map) -> case conn.params["map_identifier"] do
# Get the actual owner of the map nil ->
case User.by_id(map.owner_id, load: :characters) do # No map identifier in path - this might be a general API endpoint
{:ok, user} -> # For now, we'll return an error since we need to validate against a specific map
# Return the map owner as the authenticated user {:error, "Authentication failed", :no_map_context}
{:ok, user, map}
identifier ->
# Resolve the identifier (could be UUID or slug)
case resolve_map_identifier(identifier) do
{:ok, map} ->
# Validate the token matches this specific map's API key
if is_binary(map.public_api_key) &&
Crypto.secure_compare(map.public_api_key, token) do
# Get the map owner
case User.by_id(map.owner_id, load: :characters) do
{:ok, user} ->
{:ok, user, map}
{:error, _} ->
{:error, "Authentication failed", :map_owner_not_found}
end
else
{:error, "Authentication failed", :invalid_token_for_map}
end
{:error, _} -> {:error, _} ->
# Return generic error with specific reason for internal logging {:error, "Authentication failed", :map_not_found}
{:error, "Authentication failed", :map_owner_not_found}
end end
_ ->
# Return generic error with specific reason for internal logging
{:error, "Authentication failed", :invalid_api_key}
end end
end end
defp find_map_by_api_key(api_key) do # Helper to resolve map by ID or slug
# Import necessary modules defp resolve_map_identifier(identifier) do
import Ash.Query
alias WandererApp.Api.Map alias WandererApp.Api.Map
# Query for map with matching public API key # Try as UUID first
Map case Map.by_id(identifier) do
|> filter(public_api_key == ^api_key) {:ok, map} ->
|> Ash.read_one() {:ok, map}
_ ->
# Try as slug
Map.get_map_by_slug(identifier)
end
end end
defp get_user_role(user) do defp get_user_role(user) do

View File

@@ -75,13 +75,12 @@ defmodule WandererAppWeb.CharactersLive do
def handle_event("delete", %{"character_id" => character_id}, socket) do def handle_event("delete", %{"character_id" => character_id}, socket) do
WandererApp.Character.TrackerManager.stop_tracking(character_id) WandererApp.Character.TrackerManager.stop_tracking(character_id)
{:ok, map_user_settings} = {:ok, map_character_settings} =
WandererApp.Api.MapCharacterSettings.tracked_by_character(%{character_id: character_id}) WandererApp.Api.MapCharacterSettings.tracked_by_character(%{character_id: character_id})
map_user_settings map_character_settings
|> Enum.each(fn settings -> |> Enum.each(fn settings ->
settings {:ok, _} = WandererApp.MapCharacterSettingsRepo.untrack(settings)
|> WandererApp.Api.MapCharacterSettings.untrack()
end) end)
{:ok, updated_character} = {:ok, updated_character} =

View File

@@ -4,7 +4,7 @@ defmodule WandererAppWeb.CharactersTrackingLive do
require Logger require Logger
@impl true @impl true
def mount(_params, %{"user_id" => user_id} = _session, socket) when not is_nil(user_id) do def mount(_params, _session, socket) do
{:ok, maps} = WandererApp.Maps.get_available_maps(socket.assigns.current_user) {:ok, maps} = WandererApp.Maps.get_available_maps(socket.assigns.current_user)
{:ok, {:ok,
@@ -14,7 +14,6 @@ defmodule WandererAppWeb.CharactersTrackingLive do
characters: [], characters: [],
selected_map: nil, selected_map: nil,
selected_map_slug: nil, selected_map_slug: nil,
user_id: user_id,
maps: maps |> Enum.sort_by(& &1.name, :asc) maps: maps |> Enum.sort_by(& &1.name, :asc)
)} )}
end end
@@ -37,24 +36,22 @@ defmodule WandererAppWeb.CharactersTrackingLive do
|> assign(:page_title, "Characters Tracking") |> assign(:page_title, "Characters Tracking")
end end
defp apply_action(socket, :characters, %{"slug" => map_slug} = _params) do defp apply_action(
selected_map = socket.assigns.maps |> Enum.find(&(&1.slug == map_slug)) %{assigns: %{current_user: current_user, maps: maps}} = socket,
:characters,
{:ok, character_settings} = %{"slug" => map_slug} = _params
WandererApp.Character.Activity.get_map_character_settings(selected_map.id) ) do
selected_map = maps |> Enum.find(&(&1.slug == map_slug))
user_id = socket.assigns.user_id
socket socket
|> assign(:active_page, :characters_tracking) |> assign(:active_page, :characters_tracking)
|> assign(:page_title, "Characters Tracking") |> assign(:page_title, "Characters Tracking")
|> assign( |> assign(
selected_map: selected_map, selected_map: selected_map,
selected_map_slug: map_slug, selected_map_slug: map_slug
character_settings: character_settings
) )
|> assign_async(:characters, fn -> |> assign_async(:characters, fn ->
WandererApp.Maps.load_characters(selected_map, character_settings, user_id) WandererApp.Maps.load_characters(selected_map, current_user.id)
end) end)
end end
@@ -71,55 +68,36 @@ defmodule WandererAppWeb.CharactersTrackingLive do
end end
@impl true @impl true
def handle_event("toggle_track", %{"character_id" => character_id}, socket) do def handle_event(
"toggle_track",
%{"character_id" => character_id},
%{assigns: %{current_user: current_user}} = socket
) do
selected_map = socket.assigns.selected_map selected_map = socket.assigns.selected_map
character_settings = socket.assigns.character_settings
case character_settings |> Enum.find(&(&1.character_id == character_id)) do
nil ->
WandererApp.MapCharacterSettingsRepo.create(%{
character_id: character_id,
map_id: selected_map.id,
tracked: true
})
{:noreply, socket}
character_setting ->
case character_setting.tracked do
true ->
character_setting
|> WandererApp.MapCharacterSettingsRepo.untrack!()
WandererApp.Map.Server.untrack_characters(selected_map.id, [
character_setting.character_id
])
_ ->
character_setting
|> WandererApp.MapCharacterSettingsRepo.track!()
end
end
%{result: characters} = socket.assigns.characters %{result: characters} = socket.assigns.characters
{:ok, character_settings} = case characters |> Enum.find(&(&1.id == character_id)) do
WandererApp.Character.Activity.get_map_character_settings(selected_map.id) %{tracked: false} ->
WandererApp.MapCharacterSettingsRepo.track(%{
character_id: character_id,
map_id: selected_map.id
})
characters = %{tracked: true} ->
characters WandererApp.MapCharacterSettingsRepo.untrack(%{
|> Enum.map(fn c -> character_id: character_id,
WandererApp.Maps.map_character( map_id: selected_map.id
c, })
character_settings |> Enum.find(&(&1.character_id == c.id))
) WandererApp.Map.Server.untrack_characters(selected_map.id, [
end) character_id
])
end
{:noreply, {:noreply,
socket socket
|> assign(character_settings: character_settings)
|> assign_async(:characters, fn -> |> assign_async(:characters, fn ->
{:ok, %{characters: characters}} WandererApp.Maps.load_characters(selected_map, current_user.id)
end)} end)}
end end

View File

@@ -333,21 +333,18 @@ defmodule WandererAppWeb.MapCharactersEventHandler do
def needs_tracking_setup?( def needs_tracking_setup?(
only_tracked_characters, only_tracked_characters,
characters, characters,
character_settings,
user_permissions user_permissions
) do ) do
tracked_count = tracked_count =
characters characters
|> Enum.count(fn char -> |> Enum.count(fn char ->
setting = Enum.find(character_settings, &(&1.character_id == char.id)) char.tracked
setting && setting.tracked
end) end)
untracked_count = untracked_count =
characters characters
|> Enum.count(fn char -> |> Enum.count(fn char ->
setting = Enum.find(character_settings, &(&1.character_id == char.id)) !char.tracked
setting == nil || !setting.tracked
end) end)
user_permissions.track_character && user_permissions.track_character &&

View File

@@ -422,14 +422,11 @@ defmodule WandererAppWeb.MapCoreEventHandler do
current_user_characters |> Enum.map(& &1.id) current_user_characters |> Enum.map(& &1.id)
), ),
{:ok, map_user_settings} <- WandererApp.MapUserSettingsRepo.get(map_id, current_user_id), {:ok, map_user_settings} <- WandererApp.MapUserSettingsRepo.get(map_id, current_user_id),
{:ok, character_settings} <-
WandererApp.Character.Activity.get_map_character_settings(map_id),
{:ok, %{characters: available_map_characters}} = {:ok, %{characters: available_map_characters}} =
WandererApp.Maps.load_characters(map, character_settings, current_user_id) do WandererApp.Maps.load_characters(map, current_user_id) do
tracked_data = tracked_data =
get_tracked_data( get_tracked_data(
available_map_characters, available_map_characters,
character_settings,
user_permissions, user_permissions,
only_tracked_characters only_tracked_characters
) )
@@ -473,15 +470,13 @@ defmodule WandererAppWeb.MapCoreEventHandler do
defp get_tracked_data( defp get_tracked_data(
available_map_characters, available_map_characters,
character_settings,
user_permissions, user_permissions,
only_tracked_characters only_tracked_characters
) do ) do
tracked_characters = tracked_characters =
available_map_characters available_map_characters
|> Enum.filter(fn char -> |> Enum.filter(fn char ->
setting = Enum.find(character_settings, &(&1.character_id == char.id)) char.tracked
setting != nil && setting.tracked == true
end) end)
all_tracked? = all_tracked? =
@@ -492,7 +487,6 @@ defmodule WandererAppWeb.MapCoreEventHandler do
MapCharactersEventHandler.needs_tracking_setup?( MapCharactersEventHandler.needs_tracking_setup?(
only_tracked_characters, only_tracked_characters,
available_map_characters, available_map_characters,
character_settings,
user_permissions user_permissions
) )

View File

@@ -3,7 +3,7 @@ defmodule WandererApp.MixProject do
@source_url "https://github.com/wanderer-industries/wanderer" @source_url "https://github.com/wanderer-industries/wanderer"
@version "1.77.1" @version "1.77.3"
def project do def project do
[ [

View File

@@ -100,7 +100,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
test "creates a new signature with valid parameters", %{conn: conn, map: map} do test "creates a new signature with valid parameters", %{conn: conn, map: map} do
signature_params = %{ signature_params = %{
"system_id" => Ecto.UUID.generate(), "solar_system_id" => 30_000_142,
"eve_id" => "ABC-123", "eve_id" => "ABC-123",
"character_eve_id" => "123456789", "character_eve_id" => "123456789",
"name" => "Test Signature", "name" => "Test Signature",
@@ -132,7 +132,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
test "handles signature creation with minimal required fields", %{conn: conn, map: map} do test "handles signature creation with minimal required fields", %{conn: conn, map: map} do
minimal_params = %{ minimal_params = %{
"system_id" => Ecto.UUID.generate(), "solar_system_id" => 30_000_143,
"eve_id" => "XYZ-456", "eve_id" => "XYZ-456",
"character_eve_id" => "987654321" "character_eve_id" => "987654321"
} }
@@ -152,7 +152,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
test "handles signature creation with all optional fields", %{conn: conn, map: map} do test "handles signature creation with all optional fields", %{conn: conn, map: map} do
complete_params = %{ complete_params = %{
"system_id" => Ecto.UUID.generate(), "solar_system_id" => 30_000_144,
"eve_id" => "DEF-789", "eve_id" => "DEF-789",
"character_eve_id" => "456789123", "character_eve_id" => "456789123",
"name" => "Complete Signature", "name" => "Complete Signature",
@@ -181,7 +181,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
map = Factory.insert(:map) map = Factory.insert(:map)
signature_params = %{ signature_params = %{
"system_id" => Ecto.UUID.generate(), "solar_system_id" => 30_000_145,
"eve_id" => "ABC-123", "eve_id" => "ABC-123",
"character_eve_id" => "123456789" "character_eve_id" => "123456789"
} }
@@ -392,11 +392,11 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
test "validates signature creation with invalid data types", %{conn: conn, map: map} do test "validates signature creation with invalid data types", %{conn: conn, map: map} do
invalid_params = [ invalid_params = [
%{"system_id" => "not-a-uuid", "eve_id" => "ABC", "character_eve_id" => "123"}, %{"solar_system_id" => "not-an-integer", "eve_id" => "ABC", "character_eve_id" => "123"},
%{"system_id" => Ecto.UUID.generate(), "eve_id" => 123, "character_eve_id" => "123"}, %{"solar_system_id" => 30_000_142, "eve_id" => 123, "character_eve_id" => "123"},
%{"system_id" => Ecto.UUID.generate(), "eve_id" => "ABC", "character_eve_id" => 123}, %{"solar_system_id" => 30_000_142, "eve_id" => "ABC", "character_eve_id" => 123},
%{ %{
"system_id" => Ecto.UUID.generate(), "solar_system_id" => 30_000_142,
"eve_id" => "ABC", "eve_id" => "ABC",
"character_eve_id" => "123", "character_eve_id" => "123",
"linked_system_id" => "not-an-integer" "linked_system_id" => "not-an-integer"
@@ -426,7 +426,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
long_string = String.duplicate("a", 1000) long_string = String.duplicate("a", 1000)
long_params = %{ long_params = %{
"system_id" => Ecto.UUID.generate(), "solar_system_id" => 30_000_146,
"eve_id" => "LONG-123", "eve_id" => "LONG-123",
"character_eve_id" => "123456789", "character_eve_id" => "123456789",
"name" => long_string, "name" => long_string,
@@ -448,7 +448,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
test "handles special characters in signature data", %{conn: conn, map: map} do test "handles special characters in signature data", %{conn: conn, map: map} do
special_params = %{ special_params = %{
"system_id" => Ecto.UUID.generate(), "solar_system_id" => 30_000_147,
"eve_id" => "ABC-123", "eve_id" => "ABC-123",
"character_eve_id" => "123456789", "character_eve_id" => "123456789",
"name" => "Special chars: àáâãäåæçèéêë", "name" => "Special chars: àáâãäåæçèéêë",
@@ -470,7 +470,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
test "handles empty string values", %{conn: conn, map: map} do test "handles empty string values", %{conn: conn, map: map} do
empty_params = %{ empty_params = %{
"system_id" => Ecto.UUID.generate(), "solar_system_id" => 30_000_148,
"eve_id" => "", "eve_id" => "",
"character_eve_id" => "", "character_eve_id" => "",
"name" => "", "name" => "",
@@ -537,7 +537,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
if length(data) > 0 do if length(data) > 0 do
signature = List.first(data) signature = List.first(data)
assert Map.has_key?(signature, "id") assert Map.has_key?(signature, "id")
assert Map.has_key?(signature, "system_id") assert Map.has_key?(signature, "solar_system_id")
assert Map.has_key?(signature, "eve_id") assert Map.has_key?(signature, "eve_id")
assert Map.has_key?(signature, "character_eve_id") assert Map.has_key?(signature, "character_eve_id")
end end
@@ -564,7 +564,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
test "created signature response structure", %{conn: conn, map: map} do test "created signature response structure", %{conn: conn, map: map} do
signature_params = %{ signature_params = %{
"system_id" => Ecto.UUID.generate(), "solar_system_id" => 30_000_149,
"eve_id" => "TEST-001", "eve_id" => "TEST-001",
"character_eve_id" => "123456789", "character_eve_id" => "123456789",
"name" => "Test Signature" "name" => "Test Signature"
@@ -582,7 +582,7 @@ defmodule WandererAppWeb.MapSystemSignatureAPIControllerTest do
case response do case response do
%{"data" => data} -> %{"data" => data} ->
# Should have signature structure # Should have signature structure
assert Map.has_key?(data, "id") or Map.has_key?(data, "system_id") assert Map.has_key?(data, "id") or Map.has_key?(data, "solar_system_id")
%{"error" => _error} -> %{"error" => _error} ->
# Error response is also valid # Error response is also valid

View File

@@ -8,7 +8,7 @@ defmodule WandererApp.Map.Operations.SignaturesTest do
describe "parameter validation" do describe "parameter validation" do
test "validates missing connection assigns for create_signature" do test "validates missing connection assigns for create_signature" do
conn = %{assigns: %{}} conn = %{assigns: %{}}
params = %{"solar_system_id" => "30000142"} params = %{"solar_system_id" => 30_000_142}
result = Signatures.create_signature(conn, params) result = Signatures.create_signature(conn, params)
assert {:error, :missing_params} = result assert {:error, :missing_params} = result
@@ -209,7 +209,7 @@ defmodule WandererApp.Map.Operations.SignaturesTest do
} }
# Test with minimal required parameters # Test with minimal required parameters
params = %{"solar_system_id" => "30000142"} params = %{"solar_system_id" => 30_000_142}
MapTestHelpers.expect_map_server_error(fn -> MapTestHelpers.expect_map_server_error(fn ->
result = Signatures.create_signature(conn, params) result = Signatures.create_signature(conn, params)
@@ -416,7 +416,7 @@ defmodule WandererApp.Map.Operations.SignaturesTest do
nil nil
] ]
params = %{"solar_system_id" => "30000142"} params = %{"solar_system_id" => 30_000_142}
Enum.each(malformed_conns, fn conn -> Enum.each(malformed_conns, fn conn ->
# This should either crash (expected) or return error # This should either crash (expected) or return error
@@ -462,17 +462,16 @@ defmodule WandererApp.Map.Operations.SignaturesTest do
tasks = tasks =
Enum.map(1..3, fn i -> Enum.map(1..3, fn i ->
Task.async(fn -> Task.async(fn ->
MapTestHelpers.expect_map_server_error(fn -> params = %{"solar_system_id" => 30_000_140 + i}
params = %{"solar_system_id" => "3000014#{i}"} result = Signatures.create_signature(conn, params)
Signatures.create_signature(conn, params) # We expect either system_not_found (system doesn't exist in test)
end) # or the MapTestHelpers would have caught the map server error
assert {:error, :system_not_found} = result
end) end)
end) end)
# All tasks should complete without crashing # All tasks should complete without crashing
Enum.each(tasks, fn task -> Enum.each(tasks, &Task.await/1)
assert Task.await(task) == :ok
end)
end end
end end
@@ -669,7 +668,7 @@ defmodule WandererApp.Map.Operations.SignaturesTest do
} }
] ]
params = %{"solar_system_id" => "30000142"} params = %{"solar_system_id" => 30_000_142}
Enum.each(assign_variations, fn assigns -> Enum.each(assign_variations, fn assigns ->
conn = %{assigns: assigns} conn = %{assigns: assigns}