From 6a5ff1be8c51534471a85f822e6d87cc0acd9206 Mon Sep 17 00:00:00 2001 From: Nate Shoemaker Date: Mon, 6 Jan 2025 14:17:13 -0800 Subject: [PATCH] Set deployment when viewing device (#1726) This work adds the ability to set a device's deployment when viewing it. If no eligible deployments are found, the UI is hidden. --- lib/nerves_hub/audit_logs/templates.ex | 13 ++++++++ lib/nerves_hub_web/live/devices/show.ex | 22 +++++++++++++ .../live/devices/show.html.heex | 21 ++++++++++-- .../nerves_hub_web/live/devices/show_test.exs | 33 +++++++++++++++++-- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/lib/nerves_hub/audit_logs/templates.ex b/lib/nerves_hub/audit_logs/templates.ex index 29918e0bb..c33077970 100644 --- a/lib/nerves_hub/audit_logs/templates.ex +++ b/lib/nerves_hub/audit_logs/templates.ex @@ -1,5 +1,9 @@ defmodule NervesHub.AuditLogs.Templates do + alias NervesHub.Accounts.User alias NervesHub.AuditLogs + alias NervesHub.AuditLogs.AuditLog + alias NervesHub.Deployments.Deployment + alias NervesHub.Devices.Device require Logger @@ -38,4 +42,13 @@ defmodule NervesHub.AuditLogs.Templates do AuditLogs.audit!(device, device, description) Logger.info("[DeviceChannel] #{description}") end + + @spec audit_device_deployment_update(User.t(), Device.t(), Deployment.t()) :: AuditLog.t() + def audit_device_deployment_update(user, device, deployment) do + AuditLogs.audit!( + user, + device, + "#{user.name} set #{device.identifier}'s deployment to #{deployment.name}" + ) + end end diff --git a/lib/nerves_hub_web/live/devices/show.ex b/lib/nerves_hub_web/live/devices/show.ex index 3522454d9..34e08bce3 100644 --- a/lib/nerves_hub_web/live/devices/show.ex +++ b/lib/nerves_hub_web/live/devices/show.ex @@ -4,6 +4,8 @@ defmodule NervesHubWeb.Live.Devices.Show do require Logger alias NervesHub.AuditLogs + alias NervesHub.AuditLogs.Templates + alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Devices.Alarms alias NervesHub.Devices.Connections @@ -54,6 +56,7 @@ defmodule NervesHubWeb.Live.Devices.Show do |> schedule_health_check_timer() |> assign(:fwup_progress, nil) |> audit_log_assigns(1) + |> assign(:eligible_deployments, Deployments.matching_deployments(device)) |> ok() end @@ -256,6 +259,23 @@ defmodule NervesHubWeb.Live.Devices.Show do {:noreply, clear_flash(socket, String.to_existing_atom(key_str))} end + def handle_event( + "set-deployment", + %{"deployment_id" => deployment_id}, + %{assigns: %{user: user, device: device, eligible_deployments: eligible_deployments}} = + socket + ) do + deployment = Enum.find(eligible_deployments, &(&1.id == String.to_integer(deployment_id))) + device = Devices.update_deployment(device, deployment) + _ = Templates.audit_device_deployment_update(user, device, deployment) + + socket + |> assign(:device, device) + |> assign(:deployment, deployment) + |> put_flash(:info, "Deployment successfully updated") + |> noreply() + end + def handle_event("push-update", %{"uuid" => uuid}, socket) do authorized!(:"device:push-update", socket.assigns.org_user) @@ -362,6 +382,8 @@ defmodule NervesHubWeb.Live.Devices.Show do socket |> assign(:device, device) |> assign(:deployment, nil) + |> assign(:eligible_deployments, Deployments.matching_deployments(device)) + |> put_flash(:info, "Device successfully removed from the deployment") |> noreply() end diff --git a/lib/nerves_hub_web/live/devices/show.html.heex b/lib/nerves_hub_web/live/devices/show.html.heex index 27d9684c5..4d39aa048 100644 --- a/lib/nerves_hub_web/live/devices/show.html.heex +++ b/lib/nerves_hub_web/live/devices/show.html.heex @@ -217,7 +217,7 @@

Deployments

-
+
Assigned Deployment
@@ -235,7 +235,24 @@ No Assigned Deployment
-
+
+
+ Eligible Deployments +
+
+
+ + +
+
+
+ +
Update available diff --git a/test/nerves_hub_web/live/devices/show_test.exs b/test/nerves_hub_web/live/devices/show_test.exs index 05de17327..5f85b48ed 100644 --- a/test/nerves_hub_web/live/devices/show_test.exs +++ b/test/nerves_hub_web/live/devices/show_test.exs @@ -422,7 +422,7 @@ defmodule NervesHubWeb.Live.Devices.ShowTest do end describe "clearing deployment" do - test "clears deployment", %{ + test "clears deployment and eligible deployments list is refreshed", %{ conn: conn, org: org, product: product, @@ -436,8 +436,12 @@ defmodule NervesHubWeb.Live.Devices.ShowTest do conn |> visit("/org/#{org.name}/#{product.name}/devices/#{device.identifier}") - |> click_button("Remove From Deployment") - |> assert_has("span", text: "No Assigned Deployment") + |> unwrap(fn view -> + render_change(view, "remove-from-deployment") + end) + |> assert_has("div", text: "Eligible Deployments") + + refute Repo.reload(device) |> Map.get(:deployment_id) end test "cannot clear deployment if no deployment is set", %{ @@ -452,6 +456,29 @@ defmodule NervesHubWeb.Live.Devices.ShowTest do end end + describe "setting deployment" do + test "sets deployment and creates audit", %{ + conn: conn, + org: org, + product: product, + device: device, + deployment: deployment + } do + assert length(AuditLogs.logs_for(device)) == 0 + + conn + |> visit("/org/#{org.name}/#{product.name}/devices/#{device.identifier}") + |> assert_has("div", text: "Eligible Deployment") + |> unwrap(fn view -> + render_change(view, "set-deployment", %{"deployment_id" => deployment.id}) + end) + |> assert_has("div", text: "Assigned Deployment") + + assert Repo.reload(device) |> Map.get(:deployment_id) + assert length(AuditLogs.logs_for(device)) == 1 + end + end + def device_show_path(%{device: device, org: org, product: product}) do ~p"/org/#{org.name}/#{product.name}/devices/#{device.identifier}" end