From bbd74e895fc0900e151053ed8ea313eca6e86eec Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Thu, 12 Mar 2026 14:31:31 -0500 Subject: [PATCH] fix: prevent credential inputs resetting on re-render for all integrations All non-NetBox provider forms (Sonar, Splynx, Gaiia, Preseem, PagerDuty, VISP) had the same bug as NetBox: credential inputs used value= bindings reading from saved DB state. Any phx-click event (Test Connection, etc) triggering a re-render would wipe unsaved typed values. Fix by adding @integration_credentials assign, initialized from saved integration when opening config and updated from form params on every phx-change via validate_integration. --- lib/towerops_web/live/org/settings_live.ex | 73 +++++++++++++++---- .../live/org/settings_live.html.heex | 12 +-- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/lib/towerops_web/live/org/settings_live.ex b/lib/towerops_web/live/org/settings_live.ex index 1deab69b..09aa8ea2 100644 --- a/lib/towerops_web/live/org/settings_live.ex +++ b/lib/towerops_web/live/org/settings_live.ex @@ -181,6 +181,7 @@ defmodule ToweropsWeb.Org.SettingsLive do |> assign(:integration_form, nil) |> assign(:test_result, nil) |> assign(:netbox_config, default_netbox_config()) + |> assign(:integration_credentials, %{}) |> assign(:timezone, socket.assigns.current_scope.timezone)} end @@ -404,7 +405,7 @@ defmodule ToweropsWeb.Org.SettingsLive do if provider == "netbox" do assign(socket, :netbox_config, load_netbox_config(integration)) else - socket + assign(socket, :integration_credentials, load_integration_credentials(integration, provider)) end {:noreply, @@ -440,21 +441,23 @@ defmodule ToweropsWeb.Org.SettingsLive do |> Map.put(:action, :validate) socket = - if socket.assigns.configuring == "netbox" do - assign(socket, :netbox_config, %{ - "url" => Map.get(params, "url", ""), - "api_token" => Map.get(params, "api_token", ""), - "sync_direction" => Map.get(params, "sync_direction", "pull"), - "sync_devices" => Map.get(params, "sync_devices", "true") == "true", - "sync_sites" => Map.get(params, "sync_sites", "true") == "true", - "sync_ip_addresses" => Map.get(params, "sync_ip_addresses", "false") == "true", - "sync_interfaces" => Map.get(params, "sync_interfaces", "false") == "true", - "device_role_filter" => Map.get(params, "device_role_filter", ""), - "site_filter" => Map.get(params, "site_filter", ""), - "tag_filter" => Map.get(params, "tag_filter", "") - }) - else - socket + case socket.assigns.configuring do + "netbox" -> + assign(socket, :netbox_config, %{ + "url" => Map.get(params, "url", ""), + "api_token" => Map.get(params, "api_token", ""), + "sync_direction" => Map.get(params, "sync_direction", "pull"), + "sync_devices" => Map.get(params, "sync_devices", "true") == "true", + "sync_sites" => Map.get(params, "sync_sites", "true") == "true", + "sync_ip_addresses" => Map.get(params, "sync_ip_addresses", "false") == "true", + "sync_interfaces" => Map.get(params, "sync_interfaces", "false") == "true", + "device_role_filter" => Map.get(params, "device_role_filter", ""), + "site_filter" => Map.get(params, "site_filter", ""), + "tag_filter" => Map.get(params, "tag_filter", "") + }) + + provider -> + assign(socket, :integration_credentials, extract_credentials_from_params(params, provider)) end {:noreply, assign(socket, :integration_form, to_form(changeset))} @@ -954,4 +957,42 @@ defmodule ToweropsWeb.Org.SettingsLive do end defp load_netbox_config(_), do: default_netbox_config() + + defp load_integration_credentials(integration, "sonar") do + %{ + "instance_url" => get_credential(integration, "instance_url"), + "api_token" => get_credential(integration, "api_token") + } + end + + defp load_integration_credentials(integration, "splynx") do + %{ + "instance_url" => get_credential(integration, "instance_url"), + "api_key" => get_credential(integration, "api_key"), + "api_secret" => get_credential(integration, "api_secret") + } + end + + defp load_integration_credentials(integration, _provider) do + %{"api_key" => get_credential(integration, "api_key")} + end + + defp extract_credentials_from_params(params, "sonar") do + %{ + "instance_url" => Map.get(params, "instance_url", ""), + "api_token" => Map.get(params, "api_token", "") + } + end + + defp extract_credentials_from_params(params, "splynx") do + %{ + "instance_url" => Map.get(params, "instance_url", ""), + "api_key" => Map.get(params, "api_key", ""), + "api_secret" => Map.get(params, "api_secret", "") + } + end + + defp extract_credentials_from_params(params, _provider) do + %{"api_key" => Map.get(params, "api_key", "")} + end end diff --git a/lib/towerops_web/live/org/settings_live.html.heex b/lib/towerops_web/live/org/settings_live.html.heex index bf830413..e9452fd2 100644 --- a/lib/towerops_web/live/org/settings_live.html.heex +++ b/lib/towerops_web/live/org/settings_live.html.heex @@ -1615,7 +1615,7 @@