<%= label class: "flex items-center gap-2 text-gray-600" do %>
<%= radio_button(f, :store, "session", checked: @data["store"] == "session") %> only this session
<% end %>
<%= label class: "flex items-center gap-2 text-gray-600" do %>
<%= radio_button(f, :store, "livebook", checked: @data["store"] == "livebook") %> in the Livebook app
<% end %>
<%= if Livebook.Config.feature_flag_enabled?(:hub) do %>
<%= label class: "flex items-center gap-2 text-gray-600" do %>
<%= radio_button(f, :store, "hub",
disabled: @connected_hubs == [],
checked: @data["store"] == "hub"
) %> in the Hub
<% end %>
<%= if @data["store"] == "hub" do %>
<%= select(
f,
:connected_hub,
connected_hubs_options(@connected_hubs, @data["connected_hub"]),
class: "input"
) %>
<% end %>
<% end %>
<%= live_patch("Cancel", to: @return_to, class: "button-base button-outlined-gray") %>
"""
end
defp secret_with_badge(assigns) do
~H"""
<%= @secret_name %>
<%= if @active do %>
<% end %>
<%= @stored %>
"""
end
defp grant_access_message(assigns) do
~H"""
<.remix_icon
icon="error-warning-fill"
class="align-middle text-2xl flex text-gray-100 rounded-lg py-2"
/>
There is a secret named
<%= @grant_access %>
in your Livebook app. Allow this session to access it?
"""
end
@impl true
def handle_event("save", %{"data" => data}, socket) do
with {:ok, secret} <- Livebook.Secrets.validate_secret(data),
:ok <- set_secret(socket, secret, data["store"]) do
{:noreply,
socket
|> push_patch(to: socket.assigns.return_to)
|> push_secret_selected(secret.name)}
else
{:error, %{errors: errors}} ->
{:noreply, assign(socket, errors: errors)}
{:error, socket} ->
{:noreply, socket}
end
end
def handle_event("select_secret", %{"secret_name" => secret_name}, socket) do
{:noreply,
socket |> push_patch(to: socket.assigns.return_to) |> push_secret_selected(secret_name)}
end
def handle_event("select_livebook_secret", %{"secret_name" => secret_name}, socket) do
grant_access(secret_name, socket)
{:noreply,
socket |> push_patch(to: socket.assigns.return_to) |> push_secret_selected(secret_name)}
end
def handle_event("validate", %{"data" => data}, socket) do
socket = assign(socket, data: data)
case Livebook.Secrets.validate_secret(data) do
{:ok, _} -> {:noreply, assign(socket, errors: [])}
{:error, changeset} -> {:noreply, assign(socket, errors: changeset.errors)}
end
end
def handle_event("grant_access", %{"secret_name" => secret_name}, socket) do
grant_access(secret_name, socket)
{:noreply,
socket |> push_patch(to: socket.assigns.return_to) |> push_secret_selected(secret_name)}
end
defp push_secret_selected(%{assigns: %{select_secret_ref: nil}} = socket, _), do: socket
defp push_secret_selected(%{assigns: %{select_secret_ref: ref}} = socket, secret_name) do
push_event(socket, "secret_selected", %{select_secret_ref: ref, secret_name: secret_name})
end
defp prefill_secret_name(socket) do
if unavailable_secret?(
socket.assigns.prefill_secret_name,
socket.assigns.secrets,
socket.assigns.livebook_secrets
),
do: socket.assigns.prefill_secret_name,
else: ""
end
defp unavailable_secret?(nil, _, _), do: false
defp unavailable_secret?("", _, _), do: false
defp unavailable_secret?(preselect_name, secrets, livebook_secrets) do
not Map.has_key?(secrets, preselect_name) and
not Map.has_key?(livebook_secrets, preselect_name)
end
defp title(%{assigns: %{select_secret_ref: nil}}), do: "Add secret"
defp title(%{assigns: %{select_secret_options: %{"title" => title}}}), do: title
defp title(_), do: "Select secret"
defp set_secret(socket, secret, "session") do
Livebook.Session.set_secret(socket.assigns.session.pid, secret)
end
defp set_secret(socket, secret, "livebook") do
Livebook.Secrets.set_secret(secret)
Livebook.Session.set_secret(socket.assigns.session.pid, secret)
end
defp set_secret(socket, secret, "hub") do
selected_hub = socket.assigns.data["connected_hub"]
if hub = Enum.find(socket.assigns.connected_hubs, &(&1.hub.id == selected_hub)) do
create_secret_request =
LivebookProto.CreateSecretRequest.new!(
name: secret.name,
value: secret.value
)
case EnterpriseClient.send_request(hub.pid, create_secret_request) do
{:create_secret, _} -> :ok
{:error, reason} -> {:error, put_flash(socket, :error, reason)}
end
else
{:error, %{errors: [{"connected_hub", {"can't be blank", []}}]}}
end
end
defp grant_access(secret_name, socket) do
secret_value = socket.assigns.livebook_secrets[secret_name]
secret = %{name: secret_name, value: secret_value}
set_secret(socket.assigns.session.pid, secret, "session")
end
defp livebook_only_secrets(secrets, livebook_secrets) do
Enum.reject(livebook_secrets, &(&1 in secrets)) |> Enum.sort()
end
defp must_grant_access(%{assigns: %{prefill_secret_name: prefill_secret_name}} = socket) do
if not Map.has_key?(socket.assigns.secrets, prefill_secret_name) and
Map.has_key?(socket.assigns.livebook_secrets, prefill_secret_name) do
prefill_secret_name
end
end
# TODO: Livebook.Hubs.fetch_hubs_with_secrets_storage()
defp connected_hubs_options(connected_hubs, selected_hub) do
[[key: "Select one Hub", value: "", selected: true, disabled: true]] ++
for %{hub: %{id: id, hub_name: name}} <- connected_hubs do
[key: name, value: id, selected: id == selected_hub]
end
end
end