remove-gleam (#218)

Reviewed-on: graham/towerops-web#218
This commit is contained in:
Graham McIntire 2026-03-29 11:03:20 -05:00 committed by graham
parent afb9fcc538
commit 44a80cd5eb
15 changed files with 155 additions and 3234 deletions

2
.gitignore vendored
View file

@ -100,7 +100,7 @@ profiles.json
.stride_auth.md .stride_auth.md
# Gleam build artifacts # Build artifacts
/build/ /build/
# Generated by nix, machine-specific paths # Generated by nix, machine-specific paths

View file

@ -1,5 +1,4 @@
erlang 28.3 erlang 28.3
elixir 1.19.5-otp-28 elixir 1.19.5-otp-28
#elixir 1.20.0-rc.1-otp-28 #elixir 1.20.0-rc.1-otp-28
gleam 1.15.2
nodejs 22.22.2 nodejs 22.22.2

View file

@ -34,29 +34,21 @@ RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential git curl libsnmp-dev \ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential git curl libsnmp-dev \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Install Gleam compiler
ARG GLEAM_VERSION=1.15.2
RUN curl -fsSL https://github.com/gleam-lang/gleam/releases/download/v${GLEAM_VERSION}/gleam-v${GLEAM_VERSION}-x86_64-unknown-linux-musl.tar.gz \
| tar xz -C /usr/local/bin/
# prepare build dir # prepare build dir
WORKDIR /app WORKDIR /app
# set build ENV # set build ENV
ENV MIX_ENV="prod" ENV MIX_ENV="prod"
# install hex + rebar + mix_gleam archive (must use cache mounts so they # install hex + rebar (must use cache mounts so they persist into subsequent cache-mounted steps)
# persist into subsequent cache-mounted steps)
RUN --mount=type=cache,target=/root/.hex \ RUN --mount=type=cache,target=/root/.hex \
--mount=type=cache,target=/root/.mix \ --mount=type=cache,target=/root/.mix \
mix local.hex --force \ mix local.hex --force \
&& mix local.rebar --force \ && mix local.rebar --force
&& mix archive.install hex mix_gleam --force
# install mix dependencies # install mix dependencies
COPY mix.exs mix.lock gleam.toml ./ COPY mix.exs mix.lock ./
COPY vendor vendor COPY vendor vendor
COPY src src
RUN --mount=type=cache,target=/root/.hex \ RUN --mount=type=cache,target=/root/.hex \
--mount=type=cache,target=/root/.mix \ --mount=type=cache,target=/root/.mix \
mix deps.get --only $MIX_ENV mix deps.get --only $MIX_ENV

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -29,16 +29,10 @@ RUN apt-get update \
curl \ curl \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Install Gleam compiler # Install hex + rebar (matches k8s/Dockerfile exactly)
ARG GLEAM_VERSION=1.15.2
RUN curl -fsSL https://github.com/gleam-lang/gleam/releases/download/v${GLEAM_VERSION}/gleam-v${GLEAM_VERSION}-x86_64-unknown-linux-musl.tar.gz \
| tar xz -C /usr/local/bin/
# Install hex + rebar + mix_gleam archive (matches k8s/Dockerfile exactly)
# This step takes ~10 seconds on every deploy, so we bake it in # This step takes ~10 seconds on every deploy, so we bake it in
RUN mix local.hex --force \ RUN mix local.hex --force \
&& mix local.rebar --force \ && mix local.rebar --force
&& mix archive.install hex mix_gleam --force
# Set build ENV # Set build ENV
ENV MIX_ENV="prod" ENV MIX_ENV="prod"

View file

@ -51,9 +51,6 @@ defmodule Towerops.EctoTypes.MacAddress do
MAC addresses are stored as VARCHAR(17) in the database (sufficient for MAC addresses are stored as VARCHAR(17) in the database (sufficient for
colon-separated format). No database migrations are required when adopting colon-separated format). No database migrations are required when adopting
this custom type - Ecto handles conversion transparently. this custom type - Ecto handles conversion transparently.
Parsing and formatting logic is implemented in Gleam at
`src/towerops/ecto_types/mac_address.gleam`.
""" """
use Ecto.Type use Ecto.Type
@ -79,7 +76,7 @@ defmodule Towerops.EctoTypes.MacAddress do
def cast(binary) when is_binary(binary) and byte_size(binary) == 6 do def cast(binary) when is_binary(binary) and byte_size(binary) == 6 do
# Check if it's a 6-byte binary (not a 6-character string) # Check if it's a 6-byte binary (not a 6-character string)
if String.printable?(binary) do if String.printable?(binary) do
# It's a short string, try string parsing via Gleam # It's a short string, try string parsing
cast_from_string(binary) cast_from_string(binary)
else else
# It's a binary MAC address (SNMP format) # It's a binary MAC address (SNMP format)

View file

@ -74,11 +74,11 @@ defmodule Towerops.Agent.HeartbeatMetadata do
defstruct version: "", hostname: "", uptime_seconds: 0 defstruct version: "", hostname: "", uptime_seconds: 0
def encode(%__MODULE__{} = m) do def encode(%__MODULE__{} = m) do
:towerops@proto@encode.encode_heartbeat_metadata({:heartbeat_metadata, m.version, m.hostname, m.uptime_seconds}) Towerops.Proto.TupleEncode.encode_heartbeat_metadata({:heartbeat_metadata, m.version, m.hostname, m.uptime_seconds})
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_heartbeat_metadata(binary) do case Towerops.Proto.TupleDecode.decode_heartbeat_metadata(binary) do
{:ok, {:heartbeat_metadata, version, hostname, uptime}} -> {:ok, {:heartbeat_metadata, version, hostname, uptime}} ->
%__MODULE__{version: version, hostname: hostname, uptime_seconds: uptime} %__MODULE__{version: version, hostname: hostname, uptime_seconds: uptime}
@ -93,11 +93,11 @@ defmodule Towerops.Agent.HeartbeatResponse do
defstruct status: "" defstruct status: ""
def encode(%__MODULE__{} = r) do def encode(%__MODULE__{} = r) do
:towerops@proto@encode.encode_heartbeat_response({:heartbeat_response, r.status}) Towerops.Proto.TupleEncode.encode_heartbeat_response({:heartbeat_response, r.status})
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_heartbeat_response(binary) do case Towerops.Proto.TupleDecode.decode_heartbeat_response(binary) do
{:ok, {:heartbeat_response, status}} -> %__MODULE__{status: status} {:ok, {:heartbeat_response, status}} -> %__MODULE__{status: status}
{:error, _} -> %__MODULE__{} {:error, _} -> %__MODULE__{}
end end
@ -109,7 +109,8 @@ defmodule Towerops.Agent.SnmpConfig do
defstruct enabled: false, version: "", community: "", port: 0, transport: "" defstruct enabled: false, version: "", community: "", port: 0, transport: ""
def encode(%__MODULE__{} = s) do def encode(%__MODULE__{} = s) do
:towerops@proto@encode.encode_snmp_config({:snmp_config, s.enabled, s.version, s.community, s.port, s.transport}) # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
Towerops.Proto.TupleEncode.encode_snmp_config({:snmp_config, s.enabled, s.version, s.community, s.port, s.transport})
end end
end end
@ -123,11 +124,11 @@ defmodule Towerops.Agent.Sensor do
defstruct id: "", type: "", oid: "", divisor: 0.0, unit: "", metadata: %{} defstruct id: "", type: "", oid: "", divisor: 0.0, unit: "", metadata: %{}
def encode(%__MODULE__{} = s) do def encode(%__MODULE__{} = s) do
:towerops@proto@encode.encode_sensor({:sensor, s.id, s.type, s.oid, s.divisor, s.unit, s.metadata}) Towerops.Proto.TupleEncode.encode_sensor({:sensor, s.id, s.type, s.oid, s.divisor, s.unit, s.metadata})
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_sensor(binary) do case Towerops.Proto.TupleDecode.decode_sensor(binary) do
{:ok, {:sensor, id, type, oid, divisor, unit, meta}} -> {:ok, {:sensor, id, type, oid, divisor, unit, meta}} ->
%__MODULE__{ %__MODULE__{
id: id, id: id,
@ -149,11 +150,11 @@ defmodule Towerops.Agent.Interface do
defstruct id: "", if_index: 0, if_name: "" defstruct id: "", if_index: 0, if_name: ""
def encode(%__MODULE__{} = i) do def encode(%__MODULE__{} = i) do
:towerops@proto@encode.encode_interface({:interface, i.id, i.if_index, i.if_name}) Towerops.Proto.TupleEncode.encode_interface({:interface, i.id, i.if_index, i.if_name})
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
_ = :towerops@proto@decode.decode_device(binary) _ = Towerops.Proto.TupleDecode.decode_device(binary)
%__MODULE__{} %__MODULE__{}
end end
end end
@ -191,7 +192,7 @@ defmodule Towerops.Agent.Device do
{:interface, i.id, i.if_index, i.if_name} {:interface, i.id, i.if_index, i.if_name}
end) end)
:towerops@proto@encode.encode_device( Towerops.Proto.TupleEncode.encode_device(
{:device, d.id, d.name, d.ip_address, snmp, d.poll_interval_seconds, sensors, interfaces, d.monitoring_enabled, {:device, d.id, d.name, d.ip_address, snmp, d.poll_interval_seconds, sensors, interfaces, d.monitoring_enabled,
d.check_interval_seconds} d.check_interval_seconds}
) )
@ -205,7 +206,7 @@ defmodule Towerops.Agent.AgentConfig do
defstruct version: "", poll_interval_seconds: 0, devices: [], checks: [] defstruct version: "", poll_interval_seconds: 0, devices: [], checks: []
def encode(%__MODULE__{} = c) do def encode(%__MODULE__{} = c) do
checks = Enum.map(c.checks, &Towerops.Proto.Agent.check_to_gleam/1) checks = Enum.map(c.checks, &Towerops.Proto.Agent.check_to_tuple/1)
device_tuples = device_tuples =
Enum.map(c.devices, fn %Device{} = d -> Enum.map(c.devices, fn %Device{} = d ->
@ -233,11 +234,11 @@ defmodule Towerops.Agent.AgentConfig do
end) end)
config = {:agent_config, c.version, c.poll_interval_seconds, device_tuples, checks} config = {:agent_config, c.version, c.poll_interval_seconds, device_tuples, checks}
:towerops@proto@encode.encode_agent_config(config) Towerops.Proto.TupleEncode.encode_agent_config(config)
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_agent_config(binary) do case Towerops.Proto.TupleDecode.decode_agent_config(binary) do
{:ok, {:agent_config, version, poll_interval, _devices, _checks}} -> {:ok, {:agent_config, version, poll_interval, _devices, _checks}} ->
%__MODULE__{version: version, poll_interval_seconds: poll_interval} %__MODULE__{version: version, poll_interval_seconds: poll_interval}
@ -252,7 +253,7 @@ defmodule Towerops.Agent.SensorReading do
defstruct sensor_id: "", value: 0.0, status: "", timestamp: 0 defstruct sensor_id: "", value: 0.0, status: "", timestamp: 0
def encode(%__MODULE__{} = sr) do def encode(%__MODULE__{} = sr) do
:towerops@proto@encode.encode_sensor_reading({:sensor_reading, sr.sensor_id, sr.value, sr.status, sr.timestamp}) Towerops.Proto.TupleEncode.encode_sensor_reading({:sensor_reading, sr.sensor_id, sr.value, sr.status, sr.timestamp})
end end
end end
@ -269,7 +270,7 @@ defmodule Towerops.Agent.InterfaceStat do
timestamp: 0 timestamp: 0
def encode(%__MODULE__{} = s) do def encode(%__MODULE__{} = s) do
:towerops@proto@encode.encode_interface_stat( Towerops.Proto.TupleEncode.encode_interface_stat(
{:interface_stat, s.interface_id, s.if_in_octets, s.if_out_octets, s.if_in_errors, s.if_out_errors, {:interface_stat, s.interface_id, s.if_in_octets, s.if_out_octets, s.if_in_errors, s.if_out_errors,
s.if_in_discards, s.if_out_discards, s.timestamp} s.if_in_discards, s.if_out_discards, s.timestamp}
) )
@ -292,7 +293,7 @@ defmodule Towerops.Agent.NeighborDiscovery do
timestamp: 0 timestamp: 0
def encode(%__MODULE__{} = n) do def encode(%__MODULE__{} = n) do
gleam_tuple = { tuple_data = {
:neighbor_discovery, :neighbor_discovery,
n.interface_id, n.interface_id,
n.protocol, n.protocol,
@ -307,7 +308,7 @@ defmodule Towerops.Agent.NeighborDiscovery do
n.timestamp n.timestamp
} }
:towerops@proto@encode.encode_neighbor_discovery(gleam_tuple) Towerops.Proto.TupleEncode.encode_neighbor_discovery(tuple_data)
end end
end end
@ -316,13 +317,13 @@ defmodule Towerops.Agent.MonitoringCheck do
defstruct device_id: "", status: "", response_time_ms: 0.0, timestamp: 0 defstruct device_id: "", status: "", response_time_ms: 0.0, timestamp: 0
def encode(%__MODULE__{} = mc) do def encode(%__MODULE__{} = mc) do
:towerops@proto@encode.encode_monitoring_check( Towerops.Proto.TupleEncode.encode_monitoring_check(
{:monitoring_check, mc.device_id, mc.status, mc.response_time_ms, mc.timestamp} {:monitoring_check, mc.device_id, mc.status, mc.response_time_ms, mc.timestamp}
) )
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_monitoring_check(binary) do case Towerops.Proto.TupleDecode.decode_monitoring_check(binary) do
{:ok, {:monitoring_check, device_id, status, response_time_ms, timestamp}} -> {:ok, {:monitoring_check, device_id, status, response_time_ms, timestamp}} ->
{:ok, {:ok,
%__MODULE__{ %__MODULE__{
@ -333,7 +334,7 @@ defmodule Towerops.Agent.MonitoringCheck do
}} }}
{:error, reason} -> {:error, reason} ->
{:error, Towerops.Proto.Agent.gleam_error_to_elixir(reason)} {:error, Towerops.Proto.Agent.decode_error_to_tuple(reason)}
end end
end end
end end
@ -343,8 +344,8 @@ defmodule Towerops.Agent.Metric do
defstruct metric_type: nil defstruct metric_type: nil
def encode(%__MODULE__{metric_type: mt}) do def encode(%__MODULE__{metric_type: mt}) do
gleam_metric = Towerops.Proto.Agent.metric_to_gleam(mt) metric_tuple = Towerops.Proto.Agent.metric_to_tuple(mt)
:towerops@proto@encode.encode_metric(gleam_metric) Towerops.Proto.TupleEncode.encode_metric(metric_tuple)
end end
end end
@ -353,24 +354,24 @@ defmodule Towerops.Agent.MetricBatch do
defstruct metrics: [] defstruct metrics: []
def encode(%__MODULE__{} = batch) do def encode(%__MODULE__{} = batch) do
gleam_metrics = metric_tuples =
Enum.map(batch.metrics, fn %Towerops.Agent.Metric{metric_type: mt} -> Enum.map(batch.metrics, fn %Towerops.Agent.Metric{metric_type: mt} ->
Towerops.Proto.Agent.metric_to_gleam(mt) Towerops.Proto.Agent.metric_to_tuple(mt)
end) end)
:towerops@proto@encode.encode_metric_batch({:metric_batch, gleam_metrics}) Towerops.Proto.TupleEncode.encode_metric_batch({:metric_batch, metric_tuples})
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_metric_batch(binary) do case Towerops.Proto.TupleDecode.decode_metric_batch(binary) do
{:ok, {:metric_batch, metrics}} -> {:ok, {:metric_batch, metrics}} ->
{:ok, {:ok,
%__MODULE__{ %__MODULE__{
metrics: Enum.map(metrics, &Towerops.Proto.Agent.gleam_metric_to_elixir/1) metrics: Enum.map(metrics, &Towerops.Proto.Agent.tuple_to_metric/1)
}} }}
{:error, reason} -> {:error, reason} ->
{:error, Towerops.Proto.Agent.gleam_error_to_elixir(reason)} {:error, Towerops.Proto.Agent.decode_error_to_tuple(reason)}
end end
end end
end end
@ -426,13 +427,13 @@ defmodule Towerops.Agent.CheckResult do
defstruct check_id: "", status: 0, output: "", response_time_ms: 0.0, timestamp: 0 defstruct check_id: "", status: 0, output: "", response_time_ms: 0.0, timestamp: 0
def encode(%__MODULE__{} = cr) do def encode(%__MODULE__{} = cr) do
:towerops@proto@encode.encode_check_result( Towerops.Proto.TupleEncode.encode_check_result(
{:check_result, cr.check_id, cr.status, cr.output, cr.response_time_ms, cr.timestamp} {:check_result, cr.check_id, cr.status, cr.output, cr.response_time_ms, cr.timestamp}
) )
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_check_result(binary) do case Towerops.Proto.TupleDecode.decode_check_result(binary) do
{:ok, {:check_result, check_id, status, output, response_time_ms, timestamp}} -> {:ok, {:check_result, check_id, status, output, response_time_ms, timestamp}} ->
{:ok, {:ok,
%__MODULE__{ %__MODULE__{
@ -444,7 +445,7 @@ defmodule Towerops.Agent.CheckResult do
}} }}
{:error, reason} -> {:error, reason} ->
{:error, Towerops.Proto.Agent.gleam_error_to_elixir(reason)} {:error, Towerops.Proto.Agent.decode_error_to_tuple(reason)}
end end
end end
end end
@ -454,8 +455,8 @@ defmodule Towerops.Agent.CheckList do
defstruct checks: [] defstruct checks: []
def encode(%__MODULE__{} = cl) do def encode(%__MODULE__{} = cl) do
gleam_checks = Enum.map(cl.checks, &Towerops.Proto.Agent.check_to_gleam/1) check_tuples = Enum.map(cl.checks, &Towerops.Proto.Agent.check_to_tuple/1)
:towerops@proto@encode.encode_check_list({:check_list, gleam_checks}) Towerops.Proto.TupleEncode.encode_check_list({:check_list, check_tuples})
end end
end end
@ -475,7 +476,7 @@ defmodule Towerops.Agent.SnmpDevice do
transport: "" transport: ""
def encode(%__MODULE__{} = d) do def encode(%__MODULE__{} = d) do
:towerops@proto@encode.encode_snmp_device( Towerops.Proto.TupleEncode.encode_snmp_device(
{:snmp_device, d.ip, d.community, d.version, d.port, d.v3_security_level, d.v3_username, d.v3_auth_protocol, {:snmp_device, d.ip, d.community, d.version, d.port, d.v3_security_level, d.v3_username, d.v3_auth_protocol,
d.v3_auth_password, d.v3_priv_protocol, d.v3_priv_password, d.transport} d.v3_auth_password, d.v3_priv_protocol, d.v3_priv_password, d.transport}
) )
@ -487,8 +488,8 @@ defmodule Towerops.Agent.SnmpQuery do
defstruct query_type: :GET, oids: [] defstruct query_type: :GET, oids: []
def encode(%__MODULE__{} = q) do def encode(%__MODULE__{} = q) do
:towerops@proto@encode.encode_snmp_query( Towerops.Proto.TupleEncode.encode_snmp_query(
{:snmp_query, Towerops.Proto.Agent.query_type_atom_to_gleam(q.query_type), q.oids} {:snmp_query, Towerops.Proto.Agent.query_type_to_atom(q.query_type), q.oids}
) )
end end
end end
@ -513,17 +514,17 @@ defmodule Towerops.Agent.AgentJobList do
defstruct jobs: [] defstruct jobs: []
def encode(%__MODULE__{} = jl) do def encode(%__MODULE__{} = jl) do
gleam_jobs = Enum.map(jl.jobs, &Towerops.Proto.Agent.job_to_gleam/1) job_tuples = Enum.map(jl.jobs, &Towerops.Proto.Agent.job_to_tuple/1)
:towerops@proto@encode.encode_agent_job_list({:agent_job_list, gleam_jobs}) Towerops.Proto.TupleEncode.encode_agent_job_list({:agent_job_list, job_tuples})
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_agent_job_list(binary) do case Towerops.Proto.TupleDecode.decode_agent_job_list(binary) do
{:ok, {:agent_job_list, gleam_jobs}} -> {:ok, {:agent_job_list, job_tuples}} ->
{:ok, %__MODULE__{jobs: Enum.map(gleam_jobs, &Towerops.Proto.Agent.gleam_job_to_elixir/1)}} {:ok, %__MODULE__{jobs: Enum.map(job_tuples, &Towerops.Proto.Agent.tuple_to_job/1)}}
{:error, reason} -> {:error, reason} ->
{:error, Towerops.Proto.Agent.gleam_error_to_elixir(reason)} {:error, Towerops.Proto.Agent.decode_error_to_tuple(reason)}
end end
end end
end end
@ -540,17 +541,17 @@ defmodule Towerops.Agent.AgentJob do
mikrotik_commands: [] mikrotik_commands: []
def encode(%__MODULE__{} = j) do def encode(%__MODULE__{} = j) do
gleam_job = Towerops.Proto.Agent.job_to_gleam(j) job_tuple = Towerops.Proto.Agent.job_to_tuple(j)
:towerops@proto@encode.encode_agent_job(gleam_job) Towerops.Proto.TupleEncode.encode_agent_job(job_tuple)
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_agent_job(binary) do case Towerops.Proto.TupleDecode.decode_agent_job(binary) do
{:ok, gleam_job} -> {:ok, job_tuple} ->
{:ok, Towerops.Proto.Agent.gleam_job_to_elixir(gleam_job)} {:ok, Towerops.Proto.Agent.tuple_to_job(job_tuple)}
{:error, reason} -> {:error, reason} ->
{:error, Towerops.Proto.Agent.gleam_error_to_elixir(reason)} {:error, Towerops.Proto.Agent.decode_error_to_tuple(reason)}
end end
end end
end end
@ -629,11 +630,11 @@ defmodule Towerops.Agent.AgentError do
defstruct device_id: "", job_id: "", message: "", timestamp: 0 defstruct device_id: "", job_id: "", message: "", timestamp: 0
def encode(%__MODULE__{} = e) do def encode(%__MODULE__{} = e) do
:towerops@proto@encode.encode_agent_error({:agent_error, e.device_id, e.job_id, e.message, e.timestamp}) Towerops.Proto.TupleEncode.encode_agent_error({:agent_error, e.device_id, e.job_id, e.message, e.timestamp})
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_agent_error(binary) do case Towerops.Proto.TupleDecode.decode_agent_error(binary) do
{:ok, {:agent_error, device_id, job_id, message, timestamp}} -> {:ok, {:agent_error, device_id, job_id, message, timestamp}} ->
{:ok, {:ok,
%__MODULE__{ %__MODULE__{
@ -644,7 +645,7 @@ defmodule Towerops.Agent.AgentError do
}} }}
{:error, reason} -> {:error, reason} ->
{:error, Towerops.Proto.Agent.gleam_error_to_elixir(reason)} {:error, Towerops.Proto.Agent.decode_error_to_tuple(reason)}
end end
end end
end end
@ -654,13 +655,13 @@ defmodule Towerops.Agent.CredentialTestResult do
defstruct test_id: "", success: false, error_message: "", system_description: "", timestamp: 0 defstruct test_id: "", success: false, error_message: "", system_description: "", timestamp: 0
def encode(%__MODULE__{} = r) do def encode(%__MODULE__{} = r) do
:towerops@proto@encode.encode_credential_test_result( Towerops.Proto.TupleEncode.encode_credential_test_result(
{:credential_test_result, r.test_id, r.success, r.error_message, r.system_description, r.timestamp} {:credential_test_result, r.test_id, r.success, r.error_message, r.system_description, r.timestamp}
) )
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_credential_test_result(binary) do case Towerops.Proto.TupleDecode.decode_credential_test_result(binary) do
{:ok, {:credential_test_result, test_id, success, error_message, system_description, timestamp}} -> {:ok, {:credential_test_result, test_id, success, error_message, system_description, timestamp}} ->
{:ok, {:ok,
%__MODULE__{ %__MODULE__{
@ -672,7 +673,7 @@ defmodule Towerops.Agent.CredentialTestResult do
}} }}
{:error, reason} -> {:error, reason} ->
{:error, Towerops.Proto.Agent.gleam_error_to_elixir(reason)} {:error, Towerops.Proto.Agent.decode_error_to_tuple(reason)}
end end
end end
end end
@ -694,18 +695,18 @@ defmodule Towerops.Agent.MikrotikResult do
defstruct device_id: "", job_id: "", sentences: [], error: "", timestamp: 0 defstruct device_id: "", job_id: "", sentences: [], error: "", timestamp: 0
def encode(%__MODULE__{} = r) do def encode(%__MODULE__{} = r) do
gleam_sentences = sentence_tuples =
Enum.map(r.sentences, fn %MikrotikSentence{attributes: attrs} -> Enum.map(r.sentences, fn %MikrotikSentence{attributes: attrs} ->
{:mikrotik_sentence, attrs} {:mikrotik_sentence, attrs}
end) end)
:towerops@proto@encode.encode_mikrotik_result( Towerops.Proto.TupleEncode.encode_mikrotik_result(
{:mikrotik_result, r.device_id, r.job_id, gleam_sentences, r.error, r.timestamp} {:mikrotik_result, r.device_id, r.job_id, sentence_tuples, r.error, r.timestamp}
) )
end end
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_mikrotik_result(binary) do case Towerops.Proto.TupleDecode.decode_mikrotik_result(binary) do
{:ok, {:mikrotik_result, device_id, job_id, sentences, error, timestamp}} -> {:ok, {:mikrotik_result, device_id, job_id, sentences, error, timestamp}} ->
{:ok, {:ok,
%__MODULE__{ %__MODULE__{
@ -722,7 +723,7 @@ defmodule Towerops.Agent.MikrotikResult do
}} }}
{:error, reason} -> {:error, reason} ->
{:error, Towerops.Proto.Agent.gleam_error_to_elixir(reason)} {:error, Towerops.Proto.Agent.decode_error_to_tuple(reason)}
end end
end end
end end
@ -742,7 +743,7 @@ defmodule Towerops.Agent.LldpTopologyResult do
defstruct device_id: "", job_id: "", local_system_name: "", neighbors: [], timestamp: 0 defstruct device_id: "", job_id: "", local_system_name: "", neighbors: [], timestamp: 0
def decode(binary) when is_binary(binary) do def decode(binary) when is_binary(binary) do
case :towerops@proto@decode.decode_lldp_topology_result(binary) do case Towerops.Proto.TupleDecode.decode_lldp_topology_result(binary) do
{:ok, {:lldp_topology_result, device_id, job_id, local_system_name, neighbors, timestamp}} -> {:ok, {:lldp_topology_result, device_id, job_id, local_system_name, neighbors, timestamp}} ->
{:ok, {:ok,
%__MODULE__{ %__MODULE__{
@ -763,7 +764,7 @@ defmodule Towerops.Agent.LldpTopologyResult do
}} }}
{:error, reason} -> {:error, reason} ->
{:error, Towerops.Proto.Agent.gleam_error_to_elixir(reason)} {:error, Towerops.Proto.Agent.decode_error_to_tuple(reason)}
end end
end end
end end
@ -784,24 +785,24 @@ defmodule Towerops.Proto.Agent do
alias Towerops.Agent.SnmpDevice alias Towerops.Agent.SnmpDevice
alias Towerops.Agent.SnmpQuery alias Towerops.Agent.SnmpQuery
# Convert Gleam DecodeError to Elixir {atom, string} tuple. # Convert DecodeError to Elixir {atom, string} tuple.
# Most error tags pass through unchanged; :wire_error maps to :decode_error. # Most error tags pass through unchanged; :wire_error maps to :decode_error.
def gleam_error_to_elixir({:wire_error, msg}), do: {:decode_error, msg} def decode_error_to_tuple({:wire_error, msg}), do: {:decode_error, msg}
def gleam_error_to_elixir({tag, msg}) when is_atom(tag) and is_binary(msg), do: {tag, msg} def decode_error_to_tuple({tag, msg}) when is_atom(tag) and is_binary(msg), do: {tag, msg}
def gleam_error_to_elixir(other), do: {:decode_error, inspect(other)} def decode_error_to_tuple(other), do: {:decode_error, inspect(other)}
# Convert Elixir metric tagged tuple to Gleam metric variant # Convert Elixir metric tagged tuple to metric tuple
def metric_to_gleam({:sensor_reading, %SensorReading{} = sr}) do def metric_to_tuple({:sensor_reading, %SensorReading{} = sr}) do
{:sensor_reading_metric, {:sensor_reading, sr.sensor_id, sr.value, sr.status, sr.timestamp}} {:sensor_reading_metric, {:sensor_reading, sr.sensor_id, sr.value, sr.status, sr.timestamp}}
end end
def metric_to_gleam({:interface_stat, %InterfaceStat{} = is}) do def metric_to_tuple({:interface_stat, %InterfaceStat{} = is}) do
{:interface_stat_metric, {:interface_stat_metric,
{:interface_stat, is.interface_id, is.if_in_octets, is.if_out_octets, is.if_in_errors, is.if_out_errors, {:interface_stat, is.interface_id, is.if_in_octets, is.if_out_octets, is.if_in_errors, is.if_out_errors,
is.if_in_discards, is.if_out_discards, is.timestamp}} is.if_in_discards, is.if_out_discards, is.timestamp}}
end end
def metric_to_gleam({:neighbor_discovery, %NeighborDiscovery{} = nd}) do def metric_to_tuple({:neighbor_discovery, %NeighborDiscovery{} = nd}) do
tuple = { tuple = {
:neighbor_discovery, :neighbor_discovery,
nd.interface_id, nd.interface_id,
@ -820,17 +821,17 @@ defmodule Towerops.Proto.Agent do
{:neighbor_discovery_metric, tuple} {:neighbor_discovery_metric, tuple}
end end
def metric_to_gleam({:monitoring_check, %MonitoringCheck{} = mc}) do def metric_to_tuple({:monitoring_check, %MonitoringCheck{} = mc}) do
{:monitoring_check_metric, {:monitoring_check, mc.device_id, mc.status, mc.response_time_ms, mc.timestamp}} {:monitoring_check_metric, {:monitoring_check, mc.device_id, mc.status, mc.response_time_ms, mc.timestamp}}
end end
def metric_to_gleam({:check_result, %CheckResult{} = cr}) do def metric_to_tuple({:check_result, %CheckResult{} = cr}) do
{:check_result_metric, {:check_result, cr.check_id, cr.status, cr.output, cr.response_time_ms, cr.timestamp}} {:check_result_metric, {:check_result, cr.check_id, cr.status, cr.output, cr.response_time_ms, cr.timestamp}}
end end
# Convert Gleam metric variant to Elixir metric struct # Convert metric tuple to Elixir metric struct
def gleam_metric_to_elixir(gleam_metric) do def tuple_to_metric(metric_tuple) do
case gleam_metric do case metric_tuple do
{:sensor_reading_metric, {:sensor_reading, sensor_id, value, status, timestamp}} -> {:sensor_reading_metric, {:sensor_reading, sensor_id, value, status, timestamp}} ->
%Metric{ %Metric{
metric_type: metric_type:
@ -909,7 +910,7 @@ defmodule Towerops.Proto.Agent do
end end
# Job type conversions # Job type conversions
def job_type_atom_to_gleam(atom) when is_atom(atom) do def job_type_to_atom(atom) when is_atom(atom) do
case atom do case atom do
:DISCOVER -> :discover :DISCOVER -> :discover
:POLL -> :poll :POLL -> :poll
@ -921,8 +922,8 @@ defmodule Towerops.Proto.Agent do
end end
end end
def gleam_job_type_to_atom(gleam_jt) do def atom_to_job_type(job_type_atom) do
case gleam_jt do case job_type_atom do
:discover -> :DISCOVER :discover -> :DISCOVER
:poll -> :POLL :poll -> :POLL
:mikrotik -> :MIKROTIK :mikrotik -> :MIKROTIK
@ -933,7 +934,7 @@ defmodule Towerops.Proto.Agent do
end end
end end
def query_type_atom_to_gleam(atom) do def query_type_to_atom(atom) do
case atom do case atom do
:GET -> :get :GET -> :get
:WALK -> :walk :WALK -> :walk
@ -941,30 +942,28 @@ defmodule Towerops.Proto.Agent do
end end
end end
def gleam_query_type_to_atom(gleam_qt) do def atom_to_query_type(query_type_atom) do
case gleam_qt do case query_type_atom do
:get -> :GET :get -> :GET
:walk -> :WALK :walk -> :WALK
other -> other other -> other
end end
end end
# Convert Gleam AgentJob tuple to Elixir struct # Convert AgentJob tuple to Elixir struct
def gleam_job_to_elixir( def tuple_to_job({:agent_job, job_id, job_type, device_id, snmp_device, queries, mikrotik_device, mikrotik_commands}) do
{:agent_job, job_id, job_type, device_id, snmp_device, queries, mikrotik_device, mikrotik_commands}
) do
%AgentJob{ %AgentJob{
job_id: job_id, job_id: job_id,
job_type: gleam_job_type_to_atom(job_type), job_type: atom_to_job_type(job_type),
device_id: device_id, device_id: device_id,
snmp_device: gleam_optional_snmp_device(snmp_device), snmp_device: tuple_optional_snmp_device(snmp_device),
queries: Enum.map(queries, &gleam_query_to_elixir/1), queries: Enum.map(queries, &tuple_to_query/1),
mikrotik_device: gleam_optional_mikrotik_device(mikrotik_device), mikrotik_device: tuple_optional_mikrotik_device(mikrotik_device),
mikrotik_commands: Enum.map(mikrotik_commands, &gleam_mikrotik_command_to_elixir/1) mikrotik_commands: Enum.map(mikrotik_commands, &tuple_to_mikrotik_command/1)
} }
end end
defp gleam_optional_snmp_device( defp tuple_optional_snmp_device(
{:some, {:snmp_device, ip, community, version, port, v3sl, v3u, v3ap, v3apw, v3pp, v3ppw, transport}} {:some, {:snmp_device, ip, community, version, port, v3sl, v3u, v3ap, v3apw, v3pp, v3ppw, transport}}
) do ) do
%SnmpDevice{ %SnmpDevice{
@ -982,13 +981,13 @@ defmodule Towerops.Proto.Agent do
} }
end end
defp gleam_optional_snmp_device(:none), do: nil defp tuple_optional_snmp_device(:none), do: nil
defp gleam_query_to_elixir({:snmp_query, qt, oids}) do defp tuple_to_query({:snmp_query, qt, oids}) do
%SnmpQuery{query_type: gleam_query_type_to_atom(qt), oids: oids} %SnmpQuery{query_type: atom_to_query_type(qt), oids: oids}
end end
defp gleam_optional_mikrotik_device({:some, {:mikrotik_device, ip, port, username, password, use_ssl, ssh_port}}) do defp tuple_optional_mikrotik_device({:some, {:mikrotik_device, ip, port, username, password, use_ssl, ssh_port}}) do
%MikrotikDevice{ %MikrotikDevice{
ip: ip, ip: ip,
port: port, port: port,
@ -999,14 +998,14 @@ defmodule Towerops.Proto.Agent do
} }
end end
defp gleam_optional_mikrotik_device(:none), do: nil defp tuple_optional_mikrotik_device(:none), do: nil
defp gleam_mikrotik_command_to_elixir({:mikrotik_command, command, args}) do defp tuple_to_mikrotik_command({:mikrotik_command, command, args}) do
%MikrotikCommand{command: command, args: args} %MikrotikCommand{command: command, args: args}
end end
# Convert Elixir AgentJob struct to Gleam tuple # Convert Elixir AgentJob struct to tuple
def job_to_gleam(%AgentJob{} = j) do def job_to_tuple(%AgentJob{} = j) do
snmp_device = snmp_device =
case j.snmp_device do case j.snmp_device do
nil -> nil ->
@ -1020,7 +1019,7 @@ defmodule Towerops.Proto.Agent do
queries = queries =
Enum.map(j.queries, fn %SnmpQuery{} = q -> Enum.map(j.queries, fn %SnmpQuery{} = q ->
{:snmp_query, query_type_atom_to_gleam(q.query_type), q.oids} {:snmp_query, query_type_to_atom(q.query_type), q.oids}
end) end)
mikrotik_device = mikrotik_device =
@ -1037,12 +1036,12 @@ defmodule Towerops.Proto.Agent do
{:mikrotik_command, mc.command, mc.args} {:mikrotik_command, mc.command, mc.args}
end) end)
{:agent_job, j.job_id, job_type_atom_to_gleam(j.job_type), j.device_id, snmp_device, queries, mikrotik_device, {:agent_job, j.job_id, job_type_to_atom(j.job_type), j.device_id, snmp_device, queries, mikrotik_device,
mikrotik_commands} mikrotik_commands}
end end
# Convert Elixir Check struct to Gleam tuple # Convert Elixir Check struct to tuple
def check_to_gleam(%Towerops.Agent.Check{} = c) do def check_to_tuple(%Towerops.Agent.Check{} = c) do
config = config =
cond do cond do
c.http != nil -> c.http != nil ->

View file

@ -1,7 +1,7 @@
defmodule :towerops@proto@decode do defmodule Towerops.Proto.TupleDecode do
@moduledoc """ @moduledoc """
Erlang compatibility wrapper for decoding protobuf messages. Compatibility wrapper for decoding protobuf messages.
Converts Elixir structs to Gleam-style tuples for backward compatibility. Converts Elixir structs to tagged tuples for backward compatibility.
""" """
alias Towerops.Agent.MikrotikCommand alias Towerops.Agent.MikrotikCommand
@ -81,13 +81,13 @@ defmodule :towerops@proto@decode do
def decode_mikrotik_result(binary) when is_binary(binary) do def decode_mikrotik_result(binary) when is_binary(binary) do
case Decode.decode_mikrotik_result(binary) do case Decode.decode_mikrotik_result(binary) do
{:ok, %Types.MikrotikResult{} = mr} -> {:ok, %Types.MikrotikResult{} = mr} ->
# Convert MikrotikSentence structs to Gleam tuples # Convert MikrotikSentence structs to tuples
gleam_sentences = tuple_sentences =
Enum.map(mr.sentences, fn %Types.MikrotikSentence{attributes: attrs} -> Enum.map(mr.sentences, fn %Types.MikrotikSentence{attributes: attrs} ->
{:mikrotik_sentence, attrs} {:mikrotik_sentence, attrs}
end) end)
{:ok, {:mikrotik_result, mr.device_id, mr.job_id, gleam_sentences, mr.error, mr.timestamp}} {:ok, {:mikrotik_result, mr.device_id, mr.job_id, tuple_sentences, mr.error, mr.timestamp}}
{:error, reason} -> {:error, reason} ->
{:error, reason} {:error, reason}
@ -121,26 +121,26 @@ defmodule :towerops@proto@decode do
def decode_metric_batch(binary) when is_binary(binary) do def decode_metric_batch(binary) when is_binary(binary) do
case Decode.decode_metric_batch(binary) do case Decode.decode_metric_batch(binary) do
{:ok, %Types.MetricBatch{metrics: metrics}} -> {:ok, %Types.MetricBatch{metrics: metrics}} ->
# Convert metrics to Gleam-style tuples # Convert metrics to tagged tuples
gleam_metrics = Enum.map(metrics, &metric_to_gleam/1) tuple_metrics = Enum.map(metrics, &metric_to_tuple/1)
{:ok, {:metric_batch, gleam_metrics}} {:ok, {:metric_batch, tuple_metrics}}
{:error, reason} -> {:error, reason} ->
{:error, reason} {:error, reason}
end end
end end
defp metric_to_gleam({:sensor_reading, %Types.SensorReading{} = sr}) do defp metric_to_tuple({:sensor_reading, %Types.SensorReading{} = sr}) do
{:sensor_reading_metric, {:sensor_reading, sr.sensor_id, sr.value, sr.status, sr.timestamp}} {:sensor_reading_metric, {:sensor_reading, sr.sensor_id, sr.value, sr.status, sr.timestamp}}
end end
defp metric_to_gleam({:interface_stat, %Types.InterfaceStat{} = is}) do defp metric_to_tuple({:interface_stat, %Types.InterfaceStat{} = is}) do
{:interface_stat_metric, {:interface_stat_metric,
{:interface_stat, is.interface_id, is.if_in_octets, is.if_out_octets, is.if_in_errors, is.if_out_errors, {:interface_stat, is.interface_id, is.if_in_octets, is.if_out_octets, is.if_in_errors, is.if_out_errors,
is.if_in_discards, is.if_out_discards, is.timestamp}} is.if_in_discards, is.if_out_discards, is.timestamp}}
end end
defp metric_to_gleam({:neighbor_discovery, %Types.NeighborDiscovery{} = nd}) do defp metric_to_tuple({:neighbor_discovery, %Types.NeighborDiscovery{} = nd}) do
{ {
:neighbor_discovery_metric, :neighbor_discovery_metric,
{ {
@ -177,7 +177,7 @@ defmodule :towerops@proto@decode do
def decode_agent_config(binary) when is_binary(binary) do def decode_agent_config(binary) when is_binary(binary) do
case Decode.decode_agent_config(binary) do case Decode.decode_agent_config(binary) do
{:ok, %Types.AgentConfig{} = ac} -> {:ok, %Types.AgentConfig{} = ac} ->
# Convert devices and checks to Gleam tuples (not implemented yet, return empty lists) # Convert devices and checks to tuples (not implemented yet, return empty lists)
{:ok, {:agent_config, ac.version, ac.poll_interval_seconds, [], []}} {:ok, {:agent_config, ac.version, ac.poll_interval_seconds, [], []}}
{:error, reason} -> {:error, reason} ->
@ -189,7 +189,7 @@ defmodule :towerops@proto@decode do
case Decode.decode_agent_job(binary) do case Decode.decode_agent_job(binary) do
{:ok, %Types.AgentJob{} = job} -> {:ok, %Types.AgentJob{} = job} ->
agent_job = types_job_to_agent_job(job) agent_job = types_job_to_agent_job(job)
{:ok, Towerops.Proto.Agent.job_to_gleam(agent_job)} {:ok, Towerops.Proto.Agent.job_to_tuple(agent_job)}
{:error, reason} -> {:error, reason} ->
{:error, reason} {:error, reason}
@ -199,10 +199,10 @@ defmodule :towerops@proto@decode do
def decode_agent_job_list(binary) when is_binary(binary) do def decode_agent_job_list(binary) when is_binary(binary) do
case Decode.decode_agent_job_list(binary) do case Decode.decode_agent_job_list(binary) do
{:ok, %Types.AgentJobList{jobs: jobs}} -> {:ok, %Types.AgentJobList{jobs: jobs}} ->
# Convert Types.AgentJob to Agent.AgentJob, then to Gleam tuples # Convert Types.AgentJob to Agent.AgentJob, then to tuples
agent_jobs = Enum.map(jobs, &types_job_to_agent_job/1) agent_jobs = Enum.map(jobs, &types_job_to_agent_job/1)
gleam_jobs = Enum.map(agent_jobs, &Towerops.Proto.Agent.job_to_gleam/1) tuple_jobs = Enum.map(agent_jobs, &Towerops.Proto.Agent.job_to_tuple/1)
{:ok, {:agent_job_list, gleam_jobs}} {:ok, {:agent_job_list, tuple_jobs}}
{:error, reason} -> {:error, reason} ->
{:error, reason} {:error, reason}
@ -266,13 +266,13 @@ defmodule :towerops@proto@decode do
def decode_lldp_topology_result(binary) when is_binary(binary) do def decode_lldp_topology_result(binary) when is_binary(binary) do
case Decode.decode_lldp_topology_result(binary) do case Decode.decode_lldp_topology_result(binary) do
{:ok, %Types.LldpTopologyResult{} = ltr} -> {:ok, %Types.LldpTopologyResult{} = ltr} ->
# Convert LldpNeighbor structs to Gleam tuples # Convert LldpNeighbor structs to tuples
gleam_neighbors = tuple_neighbors =
Enum.map(ltr.neighbors, fn %Types.LldpNeighbor{} = n -> Enum.map(ltr.neighbors, fn %Types.LldpNeighbor{} = n ->
{:lldp_neighbor, n.neighbor_name, n.local_port, n.remote_port, n.remote_port_id, n.management_addresses} {:lldp_neighbor, n.neighbor_name, n.local_port, n.remote_port, n.remote_port_id, n.management_addresses}
end) end)
{:ok, {:lldp_topology_result, ltr.device_id, ltr.job_id, ltr.local_system_name, gleam_neighbors, ltr.timestamp}} {:ok, {:lldp_topology_result, ltr.device_id, ltr.job_id, ltr.local_system_name, tuple_neighbors, ltr.timestamp}}
{:error, reason} -> {:error, reason} ->
{:error, reason} {:error, reason}

View file

@ -1,7 +1,7 @@
defmodule :towerops@proto@encode do defmodule Towerops.Proto.TupleEncode do
@moduledoc """ @moduledoc """
Erlang compatibility wrapper for encoding protobuf messages. Compatibility wrapper for encoding protobuf messages.
Accepts Gleam-style tuples and converts them to Elixir structs for encoding. Accepts tagged tuples and converts them to Elixir structs for encoding.
""" """
alias Towerops.Proto.Encode alias Towerops.Proto.Encode
@ -102,8 +102,8 @@ defmodule :towerops@proto@encode do
# AgentConfig # AgentConfig
def encode_agent_config({:agent_config, version, poll_interval, devices, checks}) do def encode_agent_config({:agent_config, version, poll_interval, devices, checks}) do
device_structs = Enum.map(devices, &gleam_device_to_types/1) device_structs = Enum.map(devices, &tuple_device_to_types/1)
check_structs = Enum.map(checks, &gleam_check_to_types/1) check_structs = Enum.map(checks, &tuple_check_to_types/1)
Encode.encode_agent_config(%Types.AgentConfig{ Encode.encode_agent_config(%Types.AgentConfig{
version: version, version: version,
@ -171,9 +171,9 @@ defmodule :towerops@proto@encode do
end end
# Metric # Metric
def encode_metric(gleam_metric) do def encode_metric(tuple_metric) do
metric_struct = metric_struct =
case gleam_metric do case tuple_metric do
{:sensor_reading_metric, {:sensor_reading, sensor_id, value, status, timestamp}} -> {:sensor_reading_metric, {:sensor_reading, sensor_id, value, status, timestamp}} ->
{:sensor_reading, {:sensor_reading,
%Types.SensorReading{ %Types.SensorReading{
@ -240,8 +240,8 @@ defmodule :towerops@proto@encode do
end end
# MetricBatch # MetricBatch
def encode_metric_batch({:metric_batch, gleam_metrics}) do def encode_metric_batch({:metric_batch, tuple_metrics}) do
metric_structs = Enum.map(gleam_metrics, &gleam_metric_to_types/1) metric_structs = Enum.map(tuple_metrics, &tuple_metric_to_types/1)
Encode.encode_metric_batch(%Types.MetricBatch{metrics: metric_structs}) Encode.encode_metric_batch(%Types.MetricBatch{metrics: metric_structs})
end end
@ -257,8 +257,8 @@ defmodule :towerops@proto@encode do
end end
# CheckList # CheckList
def encode_check_list({:check_list, gleam_checks}) do def encode_check_list({:check_list, tuple_checks}) do
check_structs = Enum.map(gleam_checks, &gleam_check_to_types/1) check_structs = Enum.map(tuple_checks, &tuple_check_to_types/1)
Encode.encode_check_list(%Types.CheckList{checks: check_structs}) Encode.encode_check_list(%Types.CheckList{checks: check_structs})
end end
@ -291,14 +291,14 @@ defmodule :towerops@proto@encode do
end end
# AgentJobList # AgentJobList
def encode_agent_job_list({:agent_job_list, gleam_jobs}) do def encode_agent_job_list({:agent_job_list, tuple_jobs}) do
job_structs = Enum.map(gleam_jobs, &gleam_job_to_types/1) job_structs = Enum.map(tuple_jobs, &tuple_job_to_types/1)
Encode.encode_agent_job_list(%Types.AgentJobList{jobs: job_structs}) Encode.encode_agent_job_list(%Types.AgentJobList{jobs: job_structs})
end end
# AgentJob # AgentJob
def encode_agent_job(gleam_job) do def encode_agent_job(tuple_job) do
job_struct = gleam_job_to_types(gleam_job) job_struct = tuple_job_to_types(tuple_job)
Encode.encode_agent_job(job_struct) Encode.encode_agent_job(job_struct)
end end
@ -343,8 +343,8 @@ defmodule :towerops@proto@encode do
# Helper functions for complex conversions # Helper functions for complex conversions
defp gleam_metric_to_types(gleam_metric) do defp tuple_metric_to_types(tuple_metric) do
case gleam_metric do case tuple_metric do
{:sensor_reading_metric, {:sensor_reading, sensor_id, value, status, timestamp}} -> {:sensor_reading_metric, {:sensor_reading, sensor_id, value, status, timestamp}} ->
{:sensor_reading, {:sensor_reading,
%Types.SensorReading{ %Types.SensorReading{
@ -389,7 +389,7 @@ defmodule :towerops@proto@encode do
end end
end end
defp gleam_check_to_types({:check, id, check_type, interval_seconds, timeout_ms, config}) do defp tuple_check_to_types({:check, id, check_type, interval_seconds, timeout_ms, config}) do
config_struct = config_struct =
case config do case config do
{:http_config, {:http_config,
@ -434,12 +434,12 @@ defmodule :towerops@proto@encode do
} }
end end
defp gleam_device_to_types(gleam_device) do defp tuple_device_to_types(tuple_device) do
# Stub - implement if needed # Stub - implement if needed
gleam_device tuple_device
end end
defp gleam_job_to_types( defp tuple_job_to_types(
{:agent_job, job_id, job_type, device_id, snmp_device, queries, mikrotik_device, mikrotik_commands} {:agent_job, job_id, job_type, device_id, snmp_device, queries, mikrotik_device, mikrotik_commands}
) do ) do
snmp_device_struct = snmp_device_struct =

View file

@ -6,7 +6,7 @@ defmodule Towerops.Snmp.SensorChangeDetector do
event detection across both Phoenix-polled and agent-polled devices. event detection across both Phoenix-polled and agent-polled devices.
Pure decision logic (threshold checking, value formatting) is implemented Pure decision logic (threshold checking, value formatting) is implemented
in Gleam. PubSub broadcasting, Repo queries, and event building stay here. as helper functions. PubSub broadcasting, Repo queries, and event building stay here.
""" """
alias Towerops.Snmp.Sensor alias Towerops.Snmp.Sensor

View file

@ -25,7 +25,7 @@ defmodule ToweropsWeb.ChangelogParser do
@doc """ @doc """
Parse changelog content string into a list of entries. Parse changelog content string into a list of entries.
Returns tuples in Gleam format: {:changelog_entry, date, title_opt, items} Returns internal tuples: {:changelog_entry, date, title_opt, items}
""" """
def parse_content(content) when is_binary(content) do def parse_content(content) when is_binary(content) do
content content

View file

@ -3,9 +3,8 @@
stdenv, stdenv,
mkShell, mkShell,
writeShellScriptBin, writeShellScriptBin,
# Elixir/Erlang/Gleam # Elixir/Erlang
elixir, elixir,
gleam,
# Databases and caches # Databases and caches
postgresql_16, postgresql_16,
redis, redis,
@ -217,9 +216,8 @@ mkShell {
# Development tools # Development tools
buildInputs = [ buildInputs = [
# Elixir/Erlang/Gleam # Elixir/Erlang
elixir elixir
gleam
# Databases (PostgreSQL with TimescaleDB) # Databases (PostgreSQL with TimescaleDB)
pg pg

View file

@ -3,7 +3,7 @@ defmodule Towerops.Agent.ProtoTest do
Tests for Protocol Buffer generated modules. Tests for Protocol Buffer generated modules.
These tests verify that encoding and decoding works correctly for all These tests verify that encoding and decoding works correctly for all
protobuf message types used in agent communication. The Gleam-based protobuf message types used in agent communication. The
implementation uses {:ok, struct} tuples for decode where available, implementation uses {:ok, struct} tuples for decode where available,
and some modules are encode-only. and some modules are encode-only.
""" """

View file

@ -1,4 +1,4 @@
defmodule ToweropsWeb.GleamChangelogParserTest do defmodule ToweropsWeb.ChangelogParserTest do
use ExUnit.Case, async: true use ExUnit.Case, async: true
alias ToweropsWeb.ChangelogParser alias ToweropsWeb.ChangelogParser