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.
This commit is contained in:
Graham McIntire 2026-03-12 14:31:31 -05:00
parent f980e9eb6d
commit bbd74e895f
No known key found for this signature in database
2 changed files with 63 additions and 22 deletions

View file

@ -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

View file

@ -1615,7 +1615,7 @@
<input
type="text"
name="integration[instance_url]"
value={get_credential(@integrations["sonar"], "instance_url")}
value={@integration_credentials["instance_url"]}
placeholder="https://myisp.sonar.software"
autocomplete="off"
class="block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 dark:border-white/10 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500"
@ -1628,7 +1628,7 @@
<input
type="password"
name="integration[api_token]"
value={get_credential(@integrations["sonar"], "api_token")}
value={@integration_credentials["api_token"]}
placeholder={t("Enter your Sonar API token")}
autocomplete="off"
class="block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 dark:border-white/10 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500"
@ -1745,7 +1745,7 @@
<input
type="text"
name="integration[instance_url]"
value={get_credential(@integrations["splynx"], "instance_url")}
value={@integration_credentials["instance_url"]}
placeholder="https://myisp.splynx.app"
autocomplete="off"
class="block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 dark:border-white/10 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500"
@ -1759,7 +1759,7 @@
<input
type="password"
name="integration[api_key]"
value={get_credential(@integrations["splynx"], "api_key")}
value={@integration_credentials["api_key"]}
placeholder={t("Enter your Splynx API key")}
autocomplete="off"
class="block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 dark:border-white/10 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500"
@ -1772,7 +1772,7 @@
<input
type="password"
name="integration[api_secret]"
value={get_credential(@integrations["splynx"], "api_secret")}
value={@integration_credentials["api_secret"]}
placeholder={t("Enter your Splynx API secret")}
autocomplete="off"
class="block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 dark:border-white/10 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500"
@ -1889,7 +1889,7 @@
<input
type="password"
name="integration[api_key]"
value={get_credential(@integrations[provider.id], "api_key")}
value={@integration_credentials["api_key"]}
placeholder={
if(provider.id == "pagerduty",
do: "Enter your PagerDuty integration key",