- Fixed File.read crash in devices_seeder.ex with proper error handling - Added timeout to TCP connect in aprs_is_connection.ex (10s timeout) - Fixed unsafe socket send operations with error handling - Added Process.alive? check before sending to PIDs in streaming_packets_pubsub.ex - Fixed stream materialization memory issue in packet_consumer.ex - Added error handling for datetime parsing errors - Prevented crashes from dead processes and closed sockets These fixes ensure the application is more resilient to: - Missing files - Network timeouts - Dead processes - Closed sockets - Invalid data parsing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
65 lines
1.5 KiB
Elixir
65 lines
1.5 KiB
Elixir
defmodule Aprsme.DevicesSeeder do
|
|
@moduledoc false
|
|
|
|
alias Aprsme.Devices
|
|
alias Aprsme.Repo
|
|
|
|
def seed_from_json(path \\ "test/support/test_devices.json") do
|
|
case File.read(path) do
|
|
{:ok, body} ->
|
|
case Jason.decode(body) do
|
|
{:ok, json} ->
|
|
seed_from_decoded_json(json)
|
|
|
|
{:error, reason} ->
|
|
{:error, "Failed to decode JSON: #{inspect(reason)}"}
|
|
end
|
|
|
|
{:error, reason} ->
|
|
{:error, "Failed to read file #{path}: #{inspect(reason)}"}
|
|
end
|
|
end
|
|
|
|
defp seed_from_decoded_json(json) do
|
|
tocalls = Map.get(json, "tocalls", %{})
|
|
mice = Map.get(json, "mice", %{})
|
|
micelegacy = Map.get(json, "micelegacy", %{})
|
|
now = DateTime.utc_now()
|
|
|
|
Repo.delete_all(Devices)
|
|
|
|
Enum.each([tocalls, mice, micelegacy], fn group ->
|
|
seed_device_group(group, now)
|
|
end)
|
|
|
|
{:ok, :seeded}
|
|
end
|
|
|
|
defp seed_device_group(group, now) do
|
|
Enum.each(group, fn {identifier, attrs} ->
|
|
insert_device(identifier, attrs, now)
|
|
end)
|
|
end
|
|
|
|
defp insert_device(identifier, attrs, now) do
|
|
attrs =
|
|
attrs
|
|
|> Map.put("identifier", identifier)
|
|
|> Map.update("features", nil, fn f ->
|
|
if is_list(f), do: f, else: [f]
|
|
end)
|
|
|> Map.put("updated_at", now)
|
|
|
|
%Devices{}
|
|
|> Devices.changeset(attrs)
|
|
|> Ecto.Changeset.apply_changes()
|
|
|> Map.from_struct()
|
|
|> then(fn map ->
|
|
Repo.insert!(
|
|
Devices.changeset(%Devices{}, map),
|
|
on_conflict: :replace_all,
|
|
conflict_target: :identifier
|
|
)
|
|
end)
|
|
end
|
|
end
|