Update to Phoenix LV 1.0 (#2607)

This commit is contained in:
Jonatan Kłosko 2024-05-16 18:17:45 +02:00 committed by GitHub
parent 6a45307e4c
commit 602d852b31
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 217 additions and 158 deletions

View file

@ -122,7 +122,6 @@ module.exports = {
addVariant("phx-loading", [".phx-loading&", ".phx-loading &"]); addVariant("phx-loading", [".phx-loading&", ".phx-loading &"]);
addVariant("phx-connected", [".phx-connected&", ".phx-connected &"]); addVariant("phx-connected", [".phx-connected&", ".phx-connected &"]);
addVariant("phx-error", [".phx-error&", ".phx-error &"]); addVariant("phx-error", [".phx-error&", ".phx-error &"]);
addVariant("phx-form-error", [":not(.phx-no-feedback).show-errors &"]);
addVariant("phx-click-loading", [ addVariant("phx-click-loading", [
".phx-click-loading&", ".phx-click-loading&",
".phx-click-loading &", ".phx-click-loading &",

View file

@ -19,6 +19,8 @@ defprotocol Livebook.Hubs.Provider do
""" """
@type notebook_stamp :: map() @type notebook_stamp :: map()
@type field_errors :: list({atom(), list(String.t())})
@doc """ @doc """
Transforms given hub to `Livebook.Hubs.Metadata` struct. Transforms given hub to `Livebook.Hubs.Metadata` struct.
""" """
@ -60,7 +62,7 @@ defprotocol Livebook.Hubs.Provider do
""" """
@spec create_secret(t(), Secret.t()) :: @spec create_secret(t(), Secret.t()) ::
:ok :ok
| {:error, Ecto.Changeset.t()} | {:error, field_errors()}
| {:transport_error, String.t()} | {:transport_error, String.t()}
def create_secret(hub, secret) def create_secret(hub, secret)
@ -69,7 +71,7 @@ defprotocol Livebook.Hubs.Provider do
""" """
@spec update_secret(t(), Secret.t()) :: @spec update_secret(t(), Secret.t()) ::
:ok :ok
| {:error, Ecto.Changeset.t()} | {:error, field_errors()}
| {:transport_error, String.t()} | {:transport_error, String.t()}
def update_secret(hub, secret) def update_secret(hub, secret)
@ -120,7 +122,7 @@ defprotocol Livebook.Hubs.Provider do
""" """
@spec create_file_system(t(), FileSystem.t()) :: @spec create_file_system(t(), FileSystem.t()) ::
:ok :ok
| {:error, Ecto.Changeset.t()} | {:error, field_errors()}
| {:transport_error, String.t()} | {:transport_error, String.t()}
def create_file_system(hub, file_system) def create_file_system(hub, file_system)
@ -129,7 +131,7 @@ defprotocol Livebook.Hubs.Provider do
""" """
@spec update_file_system(t(), FileSystem.t()) :: @spec update_file_system(t(), FileSystem.t()) ::
:ok :ok
| {:error, Ecto.Changeset.t()} | {:error, field_errors()}
| {:transport_error, String.t()} | {:transport_error, String.t()}
def update_file_system(hub, file_system) def update_file_system(hub, file_system)

View file

@ -212,7 +212,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Team do
def create_secret(%Team{} = team, %Secret{} = secret) do def create_secret(%Team{} = team, %Secret{} = secret) do
case Requests.create_secret(team, secret) do case Requests.create_secret(team, secret) do
{:ok, %{"id" => _}} -> :ok {:ok, %{"id" => _}} -> :ok
{:error, %{"errors" => errors}} -> {:error, add_secret_errors(secret, errors)} {:error, %{"errors" => errors}} -> {:error, parse_secret_errors(errors)}
any -> any any -> any
end end
end end
@ -220,7 +220,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Team do
def update_secret(%Team{} = team, %Secret{} = secret) do def update_secret(%Team{} = team, %Secret{} = secret) do
case Requests.update_secret(team, secret) do case Requests.update_secret(team, secret) do
{:ok, %{"id" => _}} -> :ok {:ok, %{"id" => _}} -> :ok
{:error, %{"errors" => errors}} -> {:error, add_secret_errors(secret, errors)} {:error, %{"errors" => errors}} -> {:error, parse_secret_errors(errors)}
any -> any any -> any
end end
end end
@ -228,7 +228,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Team do
def delete_secret(%Team{} = team, %Secret{} = secret) do def delete_secret(%Team{} = team, %Secret{} = secret) do
case Requests.delete_secret(team, secret) do case Requests.delete_secret(team, secret) do
{:ok, _} -> :ok {:ok, _} -> :ok
{:error, %{"errors" => errors}} -> {:error, add_secret_errors(secret, errors)} {:error, %{"errors" => errors}} -> {:error, parse_secret_errors(errors)}
any -> any any -> any
end end
end end
@ -238,7 +238,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Team do
def create_file_system(%Team{} = team, file_system) do def create_file_system(%Team{} = team, file_system) do
case Requests.create_file_system(team, file_system) do case Requests.create_file_system(team, file_system) do
{:ok, %{"id" => _}} -> :ok {:ok, %{"id" => _}} -> :ok
{:error, %{"errors" => errors}} -> {:error, add_file_system_errors(file_system, errors)} {:error, %{"errors" => errors}} -> {:error, parse_file_system_errors(file_system, errors)}
any -> any any -> any
end end
end end
@ -246,7 +246,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Team do
def update_file_system(%Team{} = team, file_system) do def update_file_system(%Team{} = team, file_system) do
case Requests.update_file_system(team, file_system) do case Requests.update_file_system(team, file_system) do
{:ok, %{"id" => _}} -> :ok {:ok, %{"id" => _}} -> :ok
{:error, %{"errors" => errors}} -> {:error, add_file_system_errors(file_system, errors)} {:error, %{"errors" => errors}} -> {:error, parse_file_system_errors(file_system, errors)}
any -> any any -> any
end end
end end
@ -254,7 +254,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Team do
def delete_file_system(%Team{} = team, file_system) do def delete_file_system(%Team{} = team, file_system) do
case Requests.delete_file_system(team, file_system) do case Requests.delete_file_system(team, file_system) do
{:ok, _} -> :ok {:ok, _} -> :ok
{:error, %{"errors" => errors}} -> {:error, add_file_system_errors(file_system, errors)} {:error, %{"errors" => errors}} -> {:error, parse_file_system_errors(file_system, errors)}
any -> any any -> any
end end
end end
@ -276,13 +276,13 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Team do
end end
end end
defp add_secret_errors(%Secret{} = secret, errors_map) do defp parse_secret_errors(errors_map) do
Requests.add_errors(secret, errors_map) Requests.to_error_list(Secret, errors_map)
end end
defp add_file_system_errors(file_system, errors_map) do defp parse_file_system_errors(%struct{} = file_system, errors_map) do
%{error_field: field} = FileSystem.external_metadata(file_system) %{error_field: field} = FileSystem.external_metadata(file_system)
errors_map = Map.new(errors_map, fn {_key, values} -> {field, values} end) errors_map = Map.new(errors_map, fn {_key, values} -> {field, values} end)
Requests.add_errors(file_system, errors_map) Requests.to_error_list(struct, errors_map)
end end
end end

View file

@ -42,19 +42,18 @@ defmodule Livebook.Teams do
defp create_org_request(%Org{} = org, attrs, callback) when is_function(callback, 1) do defp create_org_request(%Org{} = org, attrs, callback) when is_function(callback, 1) do
changeset = Org.changeset(org, attrs) changeset = Org.changeset(org, attrs)
with {:ok, %Org{} = org} <- apply_action(changeset, :insert), with {:ok, %Org{} = org} <- apply_action(changeset, :insert) do
{:ok, response} <- callback.(org) do case callback.(org) do
{:ok, response} {:ok, response} ->
else {:ok, response}
{:error, %Ecto.Changeset{} = changeset} ->
{:error, changeset}
{:error, %{"errors" => errors}} -> {:error, %{"errors" => errors}} ->
errors = map_teams_field_to_livebook_field(errors, "key_hash", "teams_key") errors = map_teams_field_to_livebook_field(errors, "key_hash", "teams_key")
{:error, add_org_errors(changeset, errors)} {:error, changeset |> add_external_errors(errors) |> Map.replace!(:action, :insert)}
any -> any ->
any any
end
end end
end end
@ -152,10 +151,6 @@ defmodule Livebook.Teams do
Plug.Crypto.KeyGenerator.generate(binary_key, "notebook secret", cache: Plug.Crypto.Keys) Plug.Crypto.KeyGenerator.generate(binary_key, "notebook secret", cache: Plug.Crypto.Keys)
end end
defp add_org_errors(%Ecto.Changeset{} = changeset, errors_map) do
Requests.add_errors(changeset, Org.__schema__(:fields), errors_map)
end
@doc """ @doc """
Returns an `%Ecto.Changeset{}` for tracking deployment group changes. Returns an `%Ecto.Changeset{}` for tracking deployment group changes.
""" """
@ -167,15 +162,27 @@ defmodule Livebook.Teams do
@doc """ @doc """
Creates a Deployment Group. Creates a Deployment Group.
""" """
@spec create_deployment_group(Team.t(), DeploymentGroup.t()) :: @spec create_deployment_group(Team.t(), map()) ::
{:ok, pos_integer()} {:ok, DeploymentGroup.t()}
| {:error, Ecto.Changeset.t()} | {:error, Ecto.Changeset.t()}
| {:transport_error, String.t()} | {:transport_error, String.t()}
def create_deployment_group(%Team{} = team, deployment_group) do def create_deployment_group(%Team{} = team, attrs) do
case Requests.create_deployment_group(team, deployment_group) do changeset = DeploymentGroup.changeset(%DeploymentGroup{}, attrs)
{:ok, %{"id" => id}} -> {:ok, id}
{:error, %{"errors" => errors}} -> {:error, Requests.add_errors(deployment_group, errors)} with {:ok, %DeploymentGroup{} = deployment_group} <- apply_action(changeset, :insert) do
any -> any case Requests.create_deployment_group(team, deployment_group) do
{:ok, %{"id" => id}} ->
{:ok, %{deployment_group | id: to_string(id)}}
{:error, %{"errors" => errors}} ->
{:error,
changeset
|> add_external_errors(errors)
|> Map.replace!(:action, :insert)}
any ->
any
end
end end
end end
@ -200,10 +207,10 @@ defmodule Livebook.Teams do
:ok :ok
{:error, %{"errors" => %{"detail" => error}}} -> {:error, %{"errors" => %{"detail" => error}}} ->
{:error, Requests.add_errors(app_deployment, %{"file" => [error]})} {:error, add_external_errors(app_deployment, %{"file" => [error]})}
{:error, %{"errors" => errors}} -> {:error, %{"errors" => errors}} ->
{:error, Requests.add_errors(app_deployment, errors)} {:error, add_external_errors(app_deployment, errors)}
any -> any ->
any any
@ -233,4 +240,13 @@ defmodule Livebook.Teams do
map map
end end
end end
defp add_external_errors(%Ecto.Changeset{data: %struct{}} = changeset, errors_map) do
errors = Requests.to_error_list(struct, errors_map)
Livebook.Utils.put_changeset_errors(changeset, errors)
end
defp add_external_errors(struct, errors_map) do
struct |> Ecto.Changeset.change() |> add_external_errors(errors_map)
end
end end

View file

@ -212,22 +212,17 @@ defmodule Livebook.Teams.Requests do
end end
@doc """ @doc """
Add requests errors to a `changeset` for the given `fields`. Normalizes errors map into errors for the given schema.
""" """
def add_errors(%Ecto.Changeset{} = changeset, fields, errors_map) do @spec to_error_list(module(), %{String.t() => list(String.t())}) ::
list({atom(), list(String.t())})
def to_error_list(struct, errors_map) do
fields = struct.__schema__(:fields) |> MapSet.new()
for {key, errors} <- errors_map, for {key, errors} <- errors_map,
field = String.to_atom(key), field = String.to_atom(key),
field in fields, field in fields,
error <- errors, do: {field, errors}
reduce: changeset,
do: (acc -> Ecto.Changeset.add_error(acc, field, error))
end
@doc """
Add requests errors to a struct.
"""
def add_errors(%struct{} = value, errors_map) do
value |> Ecto.Changeset.change() |> add_errors(struct.__schema__(:fields), errors_map)
end end
@doc false @doc false

View file

@ -213,6 +213,19 @@ defmodule Livebook.Utils do
end) end)
end end
@doc """
Adds all the given errors to the changeset for the corresponding
fields.
"""
@spec put_changeset_errors(Ecto.Changeset.t(), list({atom(), list(String.t())})) ::
Ecto.Changeset.t()
def put_changeset_errors(changeset, errors) do
for {field, errors} <- errors,
error <- errors,
reduce: changeset,
do: (changeset -> Ecto.Changeset.add_error(changeset, field, error))
end
@doc ~S""" @doc ~S"""
Validates if the given string forms valid CLI flags. Validates if the given string forms valid CLI flags.

View file

@ -30,15 +30,23 @@ defmodule LivebookWeb.FormComponents do
name={@name} name={@name}
id={@id || @name} id={@id || @name}
value={Phoenix.HTML.Form.normalize_value("text", @value)} value={Phoenix.HTML.Form.normalize_value("text", @value)}
class={[input_classes(), @class]} class={[input_classes(@errors), @class]}
{@rest} {@rest}
/> />
</.field_wrapper> </.field_wrapper>
""" """
end end
defp input_classes() do defp input_classes(errors) do
"w-full px-3 py-2 bg-gray-50 text-sm font-normal border border-gray-200 rounded-lg placeholder-gray-400 text-gray-600 disabled:opacity-70 disabled:cursor-not-allowed phx-form-error:bg-red-50 phx-form-error:border-red-600 phx-form-error:text-red-600 invalid:bg-red-50 invalid:border-red-600 invalid:text-red-600" [
"w-full px-3 py-2 text-sm font-normal border rounded-lg placeholder-gray-400 disabled:opacity-70 disabled:cursor-not-allowed",
if errors == [] do
"bg-gray-50 border-gray-200 text-gray-600"
else
"bg-red-50 border-red-600 text-red-600"
end,
"invalid:bg-red-50 invalid:border-red-600 invalid:text-red-600"
]
end end
@doc """ @doc """
@ -65,7 +73,12 @@ defmodule LivebookWeb.FormComponents do
<textarea <textarea
id={@id || @name} id={@id || @name}
name={@name} name={@name}
class={[input_classes(), "resize-none tiny-scrollbar", @monospace && "font-mono", @class]} class={[
input_classes(@errors),
"resize-none tiny-scrollbar",
@monospace && "font-mono",
@class
]}
{@rest} {@rest}
><%= Phoenix.HTML.Form.normalize_value("textarea", @value) %></textarea> ><%= Phoenix.HTML.Form.normalize_value("textarea", @value) %></textarea>
</.field_wrapper> </.field_wrapper>
@ -115,7 +128,7 @@ defmodule LivebookWeb.FormComponents do
name={@name} name={@name}
id={@id || @name} id={@id || @name}
value={Phoenix.HTML.Form.normalize_value("text", @value)} value={Phoenix.HTML.Form.normalize_value("text", @value)}
class={[input_classes(), "pr-8", @class]} class={[input_classes(@errors), "pr-8", @class]}
{@rest} {@rest}
/> />
<div class="flex items-center absolute inset-y-0 right-1"> <div class="flex items-center absolute inset-y-0 right-1">
@ -184,7 +197,7 @@ defmodule LivebookWeb.FormComponents do
name={@name} name={@name}
id={@id || @name} id={@id || @name}
value={@value} value={@value}
class={input_classes()} class={input_classes(@errors)}
spellcheck="false" spellcheck="false"
maxlength="7" maxlength="7"
{@rest} {@rest}
@ -221,7 +234,7 @@ defmodule LivebookWeb.FormComponents do
assigns = assigns_from_field(assigns) assigns = assigns_from_field(assigns)
~H""" ~H"""
<div phx-feedback-for={@name} class={[@errors != [] && "show-errors"]}> <div>
<div class="flex items-center gap-1 sm:gap-3 justify-between"> <div class="flex items-center gap-1 sm:gap-3 justify-between">
<span :if={@label} class="text-gray-700 flex gap-1 items-center"> <span :if={@label} class="text-gray-700 flex gap-1 items-center">
<%= @label %> <%= @label %>
@ -281,7 +294,7 @@ defmodule LivebookWeb.FormComponents do
assigns = assigns_from_field(assigns) assigns = assigns_from_field(assigns)
~H""" ~H"""
<div phx-feedback-for={@name} class={[@errors != [] && "show-errors"]}> <div>
<label class="flex items-center gap-2 cursor-pointer"> <label class="flex items-center gap-2 cursor-pointer">
<input :if={@unchecked_value} type="hidden" value={@unchecked_value} name={@name} /> <input :if={@unchecked_value} type="hidden" value={@unchecked_value} name={@name} />
<input <input
@ -327,7 +340,7 @@ defmodule LivebookWeb.FormComponents do
assigns = assigns_from_field(assigns) assigns = assigns_from_field(assigns)
~H""" ~H"""
<div phx-feedback-for={@name} class={[@errors != [] && "show-errors"]}> <div>
<.label :if={@label} for={@id} help={@help}><%= @label %></.label> <.label :if={@label} for={@id} help={@help}><%= @label %></.label>
<div class="flex gap-4 text-gray-600"> <div class="flex gap-4 text-gray-600">
<label :for={{value, description} <- @options} class="flex items-center gap-2 cursor-pointer"> <label :for={{value, description} <- @options} class="flex items-center gap-2 cursor-pointer">
@ -371,7 +384,7 @@ defmodule LivebookWeb.FormComponents do
assigns = assigns_from_field(assigns) assigns = assigns_from_field(assigns)
~H""" ~H"""
<div phx-feedback-for={@name} class={[@errors != [] && "show-errors"]}> <div>
<.label :if={@label} for={@id} help={@help}><%= @label %></.label> <.label :if={@label} for={@id} help={@help}><%= @label %></.label>
<div class="flex"> <div class="flex">
<label <label
@ -474,7 +487,8 @@ defmodule LivebookWeb.FormComponents do
id={@id} id={@id}
name={@name} name={@name}
class={[ class={[
"w-full px-3 py-2 pr-7 appearance-none bg-gray-50 text-sm border border-gray-200 rounded-lg placeholder-gray-400 text-gray-600 phx-form-error:border-red-300 disabled:opacity-70 disabled:cursor-not-allowed", "w-full px-3 py-2 pr-7 appearance-none bg-gray-50 text-sm border rounded-lg placeholder-gray-400 text-gray-600 disabled:opacity-70 disabled:cursor-not-allowed",
if(@errors == [], do: "border-gray-200", else: "border-red-300"),
@class @class
]} ]}
{@rest} {@rest}
@ -491,9 +505,11 @@ defmodule LivebookWeb.FormComponents do
end end
defp assigns_from_field(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do defp assigns_from_field(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do
errors = if Phoenix.Component.used_input?(field), do: field.errors, else: []
assigns assigns
|> assign(field: nil, id: assigns.id || field.id) |> assign(field: nil, id: assigns.id || field.id)
|> assign(:errors, Enum.map(field.errors, &translate_error/1)) |> assign(:errors, Enum.map(errors, &translate_error(&1)))
|> assign_new(:name, fn -> field.name end) |> assign_new(:name, fn -> field.name end)
|> assign_new(:value, fn -> field.value end) |> assign_new(:value, fn -> field.value end)
end end
@ -520,7 +536,7 @@ defmodule LivebookWeb.FormComponents do
defp field_wrapper(assigns) do defp field_wrapper(assigns) do
~H""" ~H"""
<div phx-feedback-for={@name} class={[@errors != [] && "show-errors"]}> <div>
<.label :if={@label} for={@id} help={@help}><%= @label %></.label> <.label :if={@label} for={@id} help={@help}><%= @label %></.label>
<%= render_slot(@inner_block) %> <%= render_slot(@inner_block) %>
<.error :for={msg <- @errors}><%= msg %></.error> <.error :for={msg <- @errors}><%= msg %></.error>
@ -551,7 +567,7 @@ defmodule LivebookWeb.FormComponents do
def error(assigns) do def error(assigns) do
~H""" ~H"""
<p class="mt-0.5 text-red-600 text-sm hidden phx-form-error:block"> <p class="mt-0.5 text-red-600 text-sm">
<%= render_slot(@inner_block) %> <%= render_slot(@inner_block) %>
</p> </p>
""" """

View file

@ -24,12 +24,19 @@
<div class="text-gray-50 w-full"> <div class="text-gray-50 w-full">
<form method="post" class="flex flex-col w-full"> <form method="post" class="flex flex-col w-full">
<input type="hidden" value={Phoenix.Controller.get_csrf_token()} name="_csrf_token" /> <input type="hidden" value={Phoenix.Controller.get_csrf_token()} name="_csrf_token" />
<div phx-feedback-for={@auth_mode} class={[@errors != [] && "show-errors"]}> <div>
<input <input
:if={@auth_mode == :password} :if={@auth_mode == :password}
type="password" type="password"
name="password" name="password"
class="px-4 py-2 w-full text-gray-300 placeholder-gray-400 border border-gray-500 rounded-lg bg-transparent phx-form-error:border-red-600 phx-form-error:text-red-600 phx-form-error:placeholder-red-600" class={[
"px-4 py-2 w-full border rounded-lg bg-transparent",
if @errors == [] do
"border-gray-500 text-gray-300 placeholder-gray-400"
else
"border-red-600 text-red-600 placeholder-red-600"
end
]}
placeholder="Password" placeholder="Password"
autofocus autofocus
/> />
@ -37,14 +44,18 @@
:if={@auth_mode == :token} :if={@auth_mode == :token}
type="text" type="text"
name="token" name="token"
class="px-4 py-2 w-full text-gray-300 placeholder-gray-400 border border-gray-500 rounded-lg bg-transparent phx-form-error:border-red-600 phx-form-error:text-red-600 phx-form-error:placeholder-red-600" class={[
"px-4 py-2 w-full border rounded-lg bg-transparent",
if @errors == [] do
"border-gray-500 text-gray-300 placeholder-gray-400"
else
"border-red-600 text-red-600 placeholder-red-600"
end
]}
placeholder="Token" placeholder="Token"
autofocus autofocus
/> />
<span <span :for={error <- @errors} class="mt-1 text-red-600 text-sm">
:for={error <- @errors}
class="mt-1 hidden text-red-600 text-sm phx-form-error:block"
>
<%= translate_error(error) %> <%= translate_error(error) %>
</span> </span>
</div> </div>

View file

@ -41,7 +41,7 @@ defmodule LivebookWeb.AppAuthLive do
</div> </div>
<div class="text-2xl text-gray-800 w-full pt-2"> <div class="text-2xl text-gray-800 w-full pt-2">
<form class="flex flex-col space-y-4 items-center" phx-submit="authenticate"> <form class="flex flex-col space-y-4 items-center" phx-submit="authenticate">
<div phx-feedback-for="password" class={["w-[20ch]", @errors != [] && "show-errors"]}> <div class="w-[20ch]">
<.text_field <.text_field
type="password" type="password"
name="password" name="password"
@ -49,10 +49,7 @@ defmodule LivebookWeb.AppAuthLive do
placeholder="Password" placeholder="Password"
autofocus autofocus
/> />
<span <span :for={error <- @errors} class="mt-1 text-red-600 text-sm">
:for={error <- @errors}
class="mt-1 hidden text-red-600 text-sm phx-form-error:block"
>
<%= translate_error(error) %> <%= translate_error(error) %>
</span> </span>
</div> </div>

View file

@ -120,7 +120,7 @@ defmodule LivebookWeb.Hub.FileSystemFormComponent do
with {:ok, file_system} <- Ecto.Changeset.apply_action(changeset, :update), with {:ok, file_system} <- Ecto.Changeset.apply_action(changeset, :update),
:ok <- check_file_system_connectivity(file_system), :ok <- check_file_system_connectivity(file_system),
:ok <- save_file_system(file_system, socket) do :ok <- save_file_system(file_system, changeset, socket) do
message = message =
case socket.assigns.mode do case socket.assigns.mode do
:new -> "File storage added successfully" :new -> "File storage added successfully"
@ -152,10 +152,18 @@ defmodule LivebookWeb.Hub.FileSystemFormComponent do
end end
end end
defp save_file_system(file_system, socket) do defp save_file_system(file_system, changeset, socket) do
case socket.assigns.mode do result =
:new -> Livebook.Hubs.create_file_system(socket.assigns.hub, file_system) case socket.assigns.mode do
:edit -> Livebook.Hubs.update_file_system(socket.assigns.hub, file_system) :new -> Livebook.Hubs.create_file_system(socket.assigns.hub, file_system)
:edit -> Livebook.Hubs.update_file_system(socket.assigns.hub, file_system)
end
with {:error, errors} <- result do
{:error,
changeset
|> Livebook.Utils.put_changeset_errors(errors)
|> Map.replace!(:action, :validate)}
end end
end end

View file

@ -249,8 +249,6 @@ defmodule LivebookWeb.Hub.NewLive do
|> assign_form(changeset)} |> assign_form(changeset)}
{:error, changeset} -> {:error, changeset} ->
changeset = Map.replace!(changeset, :action, :validate)
{:noreply, assign_form(socket, changeset)} {:noreply, assign_form(socket, changeset)}
{:transport_error, message} -> {:transport_error, message} ->

View file

@ -7,25 +7,23 @@ defmodule LivebookWeb.Hub.SecretFormComponent do
@impl true @impl true
def mount(socket) do def mount(socket) do
{:ok, assign(socket, changeset: nil, error_message: nil)} {:ok, assign(socket, error_message: nil)}
end end
@impl true @impl true
def update(assigns, socket) do def update(assigns, socket) do
changeset =
socket.assigns.changeset ||
Secrets.change_secret(%Secret{}, %{
name: assigns.secret_name,
value: assigns.secret_value
})
{:ok, {:ok,
socket socket
|> assign(assigns) |> assign(assigns)
|> assign_new(:changeset, fn ->
Secrets.change_secret(%Secret{}, %{
name: assigns.secret_name,
value: assigns.secret_value
})
end)
|> assign( |> assign(
title: title(socket), title: title(socket),
button: button_attrs(socket), button: button_attrs(socket),
changeset: changeset,
deployment_group_id: assigns[:deployment_group_id] deployment_group_id: assigns[:deployment_group_id]
)} )}
end end
@ -91,8 +89,10 @@ defmodule LivebookWeb.Hub.SecretFormComponent do
@impl true @impl true
def handle_event("save", %{"secret" => attrs}, socket) do def handle_event("save", %{"secret" => attrs}, socket) do
with {:ok, secret} <- Secrets.update_secret(%Secret{}, attrs), changeset = Secrets.change_secret(%Secret{}, attrs)
:ok <- set_secret(socket, secret) do
with {:ok, secret} <- Ecto.Changeset.apply_action(changeset, :insert),
:ok <- save_secret(socket, secret, changeset) do
message = message =
if socket.assigns.secret_name, if socket.assigns.secret_name,
do: "Secret #{secret.name} updated successfully", do: "Secret #{secret.name} updated successfully",
@ -103,8 +103,8 @@ defmodule LivebookWeb.Hub.SecretFormComponent do
|> put_flash(:success, message) |> put_flash(:success, message)
|> push_patch(to: socket.assigns.return_to)} |> push_patch(to: socket.assigns.return_to)}
else else
{:error, changeset} -> {:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, changeset: Map.replace!(changeset, :action, :validate))} {:noreply, assign(socket, changeset: changeset)}
{:transport_error, error} -> {:transport_error, error} ->
{:noreply, assign(socket, error_message: error)} {:noreply, assign(socket, error_message: error)}
@ -126,12 +126,20 @@ defmodule LivebookWeb.Hub.SecretFormComponent do
defp button_attrs(%{assigns: %{secret_name: nil}}), do: %{icon: "add-line", label: "Add"} defp button_attrs(%{assigns: %{secret_name: nil}}), do: %{icon: "add-line", label: "Add"}
defp button_attrs(_), do: %{icon: "save-line", label: "Save"} defp button_attrs(_), do: %{icon: "save-line", label: "Save"}
defp set_secret(%{assigns: %{secret_name: nil}} = socket, %Secret{} = secret) do defp save_secret(socket, secret, changeset) do
Hubs.create_secret(socket.assigns.hub, secret) result =
end if socket.assigns.secret_name do
Hubs.update_secret(socket.assigns.hub, secret)
else
Hubs.create_secret(socket.assigns.hub, secret)
end
defp set_secret(socket, %Secret{} = secret) do with {:error, errors} <- result do
Hubs.update_secret(socket.assigns.hub, secret) {:error,
changeset
|> Livebook.Utils.put_changeset_errors(errors)
|> Map.replace!(:action, :validate)}
end
end end
defp secret_name_input_class(nil), do: "uppercase" defp secret_name_input_class(nil), do: "uppercase"

View file

@ -23,7 +23,7 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do
%{} %{}
end end
{:ok, assign_form(socket, change_deployment_group(socket, attrs))} {:ok, assign_form(socket, Teams.change_deployment_group(%DeploymentGroup{}, attrs))}
end end
end end
@ -88,6 +88,8 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do
phx-debounce phx-debounce
/> />
<.hidden_field field={@form[:hub_id]} value={@hub.id} />
<LivebookWeb.AppComponents.deployment_group_form_content hub={@hub} form={@form} /> <LivebookWeb.AppComponents.deployment_group_form_content hub={@hub} form={@form} />
<div class="flex space-x-2"> <div class="flex space-x-2">
@ -145,10 +147,7 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do
@impl true @impl true
def handle_event("save", %{"deployment_group" => attrs}, socket) do def handle_event("save", %{"deployment_group" => attrs}, socket) do
changeset = change_deployment_group(socket, attrs) with {:ok, _deployment_group} <- Teams.create_deployment_group(socket.assigns.hub, attrs) do
with {:ok, deployment_group} <- Ecto.Changeset.apply_action(changeset, :update),
{:ok, _id} <- Teams.create_deployment_group(socket.assigns.hub, deployment_group) do
if return_to = socket.assigns.return_to do if return_to = socket.assigns.return_to do
{:noreply, {:noreply,
socket socket
@ -159,7 +158,7 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do
end end
else else
{:error, %Ecto.Changeset{} = changeset} -> {:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign_form(socket, Map.replace!(changeset, :action, :validate))} {:noreply, assign_form(socket, changeset)}
{:transport_error, message} -> {:transport_error, message} ->
{:noreply, assign(socket, error_message: message)} {:noreply, assign(socket, error_message: message)}
@ -171,14 +170,9 @@ defmodule LivebookWeb.Hub.Teams.DeploymentGroupFormComponent do
def handle_event("validate", %{"deployment_group" => attrs}, socket) do def handle_event("validate", %{"deployment_group" => attrs}, socket) do
changeset = changeset =
change_deployment_group(socket, attrs) Teams.change_deployment_group(%DeploymentGroup{}, attrs)
|> Map.replace!(:action, :validate) |> Map.replace!(:action, :validate)
{:noreply, assign_form(socket, changeset)} {:noreply, assign_form(socket, changeset)}
end end
defp change_deployment_group(socket, attrs) do
%DeploymentGroup{hub_id: socket.assigns.hub.id}
|> Teams.change_deployment_group(attrs)
end
end end

View file

@ -216,14 +216,18 @@ defmodule LivebookWeb.SessionLive.SecretsComponent do
@impl true @impl true
def handle_event("save", %{"secret" => attrs}, socket) do def handle_event("save", %{"secret" => attrs}, socket) do
with {:ok, secret} <- Secrets.update_secret(%Secret{}, attrs), changeset = Secrets.change_secret(%Secret{}, attrs)
:ok <- set_secret(socket, secret) do
with {:ok, secret} <- Ecto.Changeset.apply_action(changeset, :insert),
:ok <- save_secret(socket, secret, changeset) do
Session.set_secret(socket.assigns.session.pid, secret)
{:noreply, {:noreply,
socket socket
|> push_patch(to: socket.assigns.return_to) |> push_patch(to: socket.assigns.return_to)
|> push_secret_selected(secret.name)} |> push_secret_selected(secret.name)}
else else
{:error, changeset} -> {:error, %Ecto.Changeset{} = changeset} ->
{:noreply, assign(socket, changeset: changeset)} {:noreply, assign(socket, changeset: changeset)}
{:transport_error, error} -> {:transport_error, error} ->
@ -271,13 +275,16 @@ defmodule LivebookWeb.SessionLive.SecretsComponent do
defp title(%{assigns: %{select_secret_metadata: %{options: %{"title" => title}}}}), do: title defp title(%{assigns: %{select_secret_metadata: %{options: %{"title" => title}}}}), do: title
defp title(_), do: "Select secret" defp title(_), do: "Select secret"
defp set_secret(socket, %Secret{hub_id: nil} = secret) do defp save_secret(socket, secret, changeset) do
Session.set_secret(socket.assigns.session.pid, secret) if secret.hub_id do
end with {:error, errors} <- Hubs.create_secret(socket.assigns.hub, secret) do
{:error,
defp set_secret(socket, %Secret{} = secret) do changeset
with :ok <- Hubs.create_secret(socket.assigns.hub, secret) do |> Livebook.Utils.put_changeset_errors(errors)
Session.set_secret(socket.assigns.session.pid, secret) |> Map.replace!(:action, :validate)}
end
else
:ok
end end
end end

View file

@ -99,7 +99,7 @@ defmodule Livebook.MixProject do
defp deps do defp deps do
[ [
{:phoenix, "~> 1.7.8"}, {:phoenix, "~> 1.7.8"},
{:phoenix_live_view, "~> 0.20.2"}, {:phoenix_live_view, "~> 1.0.0-rc.0", override: true},
{:phoenix_html, "~> 4.0"}, {:phoenix_html, "~> 4.0"},
{:phoenix_live_dashboard, "~> 0.8.0"}, {:phoenix_live_dashboard, "~> 0.8.0"},
{:telemetry_metrics, "~> 1.0"}, {:telemetry_metrics, "~> 1.0"},

View file

@ -36,7 +36,7 @@
"phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"}, "phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.3", "7ff51c9b6609470f681fbea20578dede0e548302b0c8bdf338b5a753a4f045bf", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "f9470a0a8bae4f56430a23d42f977b5a6205fdba6559d76f932b876bfaec652d"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.3", "7ff51c9b6609470f681fbea20578dede0e548302b0c8bdf338b5a753a4f045bf", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "f9470a0a8bae4f56430a23d42f977b5a6205fdba6559d76f932b876bfaec652d"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.20.14", "70fa101aa0539e81bed4238777498f6215e9dda3461bdaa067cad6908110c364", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "82f6d006c5264f979ed5eb75593d808bbe39020f20df2e78426f4f2d570e2402"}, "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.0-rc.0", "42a5a568d7105d1d4e6542badfe061154491da584836a4a4ead148a2c67d649e", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "10cb32643a0cfecb6f3f1feff8211273ba5917891e451b635abc940e3b907a4a"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"plug": {:git, "https://github.com/elixir-plug/plug.git", "0574733fb933e4a2ea78532e38e687d9cffb4858", []}, "plug": {:git, "https://github.com/elixir-plug/plug.git", "0574733fb933e4a2ea78532e38e687d9cffb4858", []},

View file

@ -70,21 +70,20 @@ defmodule Livebook.HubsTest do
value = hub.id value = hub.id
secret = build(:secret, name: name, value: value, hub_id: hub.id) secret = build(:secret, name: name, value: value, hub_id: hub.id)
assert Hubs.create_secret(hub, secret) == :ok assert :ok = Hubs.create_secret(hub, secret)
assert secret in Hubs.get_secrets(hub) assert secret in Hubs.get_secrets(hub)
# Guarantee uniqueness # Guarantee uniqueness
assert {:error, changeset} = Hubs.create_secret(hub, secret) assert {:error, errors} = Hubs.create_secret(hub, secret)
assert "has already been taken" in errors_on(changeset).name assert "has already been taken" in errors[:name]
end end
test "returns changeset errors when data is invalid", %{user: user, node: node} do test "returns changeset errors when data is invalid", %{user: user, node: node} do
hub = connect_to_teams(user, node) hub = connect_to_teams(user, node)
secret = build(:secret, name: "LB_FOO", value: "BAR", hub_id: hub.id) secret = build(:secret, name: "LB_FOO", value: "BAR", hub_id: hub.id)
assert {:error, changeset} = Hubs.create_secret(hub, secret) assert {:error, errors} = Hubs.create_secret(hub, secret)
assert "cannot start with the LB_ prefix" in errors_on(changeset).name assert "cannot start with the LB_ prefix" in errors[:name]
refute secret in Hubs.get_secrets(hub)
end end
end end
@ -119,8 +118,8 @@ defmodule Livebook.HubsTest do
updated_secret = Map.replace!(secret, :value, "") updated_secret = Map.replace!(secret, :value, "")
assert {:error, changeset} = Hubs.update_secret(hub, updated_secret) assert {:error, errors} = Hubs.update_secret(hub, updated_secret)
assert "can't be blank" in errors_on(changeset).value assert "can't be blank" in errors[:value]
end end
end end
@ -156,16 +155,16 @@ defmodule Livebook.HubsTest do
assert_receive {:file_system_created, %{bucket_url: ^bucket_url, region: ^region}} assert_receive {:file_system_created, %{bucket_url: ^bucket_url, region: ^region}}
# Guarantee uniqueness # Guarantee uniqueness
assert {:error, changeset} = Hubs.create_file_system(hub, file_system) assert {:error, errors} = Hubs.create_file_system(hub, file_system)
assert "has already been taken" in errors_on(changeset).bucket_url assert "has already been taken" in errors[:bucket_url]
end end
test "returns changeset errors when data is invalid", %{user: user, node: node} do test "returns changeset errors when data is invalid", %{user: user, node: node} do
hub = connect_to_teams(user, node) hub = connect_to_teams(user, node)
file_system = build(:fs_s3, bucket_url: nil) file_system = build(:fs_s3, bucket_url: nil)
assert {:error, changeset} = Hubs.create_file_system(hub, file_system) assert {:error, errors} = Hubs.create_file_system(hub, file_system)
assert "can't be blank" in errors_on(changeset).bucket_url assert "can't be blank" in errors[:bucket_url]
end end
end end
@ -202,8 +201,8 @@ defmodule Livebook.HubsTest do
update_file_system = Map.replace!(file_system, :bucket_url, "") update_file_system = Map.replace!(file_system, :bucket_url, "")
assert {:error, changeset} = Hubs.update_file_system(hub, update_file_system) assert {:error, errors} = Hubs.update_file_system(hub, update_file_system)
assert "can't be blank" in errors_on(changeset).bucket_url assert "can't be blank" in errors[:bucket_url]
end end
end end

View file

@ -170,34 +170,32 @@ defmodule Livebook.TeamsTest do
test "creates a new deployment group when the data is valid", %{user: user, node: node} do test "creates a new deployment group when the data is valid", %{user: user, node: node} do
team = connect_to_teams(user, node) team = connect_to_teams(user, node)
deployment_group = attrs = params_for(:deployment_group, name: "DEPLOYMENT_GROUP_#{team.id}", mode: :online)
build(:deployment_group, name: "DEPLOYMENT_GROUP_#{team.id}", mode: :online)
assert {:ok, id} = Teams.create_deployment_group(team, deployment_group) assert {:ok, deployment_group} = Teams.create_deployment_group(team, attrs)
%{name: name, mode: mode} = deployment_group %{id: id, name: name, mode: mode} = deployment_group
id = to_string(id)
assert_receive {:deployment_group_created, %{id: ^id, name: ^name, mode: ^mode}} assert_receive {:deployment_group_created, %{id: ^id, name: ^name, mode: ^mode}}
# Guarantee uniqueness # Guarantee uniqueness
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group) assert {:error, changeset} = Teams.create_deployment_group(team, attrs)
assert "has already been taken" in errors_on(changeset).name assert "has already been taken" in errors_on(changeset).name
end end
test "returns changeset errors when the name is invalid", %{user: user, node: node} do test "returns changeset errors when the name is invalid", %{user: user, node: node} do
team = connect_to_teams(user, node) team = connect_to_teams(user, node)
deployment_group = %{build(:deployment_group) | name: ""} attrs = params_for(:deployment_group, name: "")
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group) assert {:error, changeset} = Teams.create_deployment_group(team, attrs)
assert "can't be blank" in errors_on(changeset).name assert "can't be blank" in errors_on(changeset).name
end end
test "returns changeset errors when the mode is invalid", %{user: user, node: node} do test "returns changeset errors when the mode is invalid", %{user: user, node: node} do
team = connect_to_teams(user, node) team = connect_to_teams(user, node)
deployment_group = %{build(:deployment_group) | mode: "invalid"} attrs = params_for(:deployment_group, mode: "invalid")
assert {:error, changeset} = Teams.create_deployment_group(team, deployment_group) assert {:error, changeset} = Teams.create_deployment_group(team, attrs)
assert "is invalid" in errors_on(changeset).mode assert "is invalid" in errors_on(changeset).mode
end end
end end
@ -206,10 +204,9 @@ defmodule Livebook.TeamsTest do
@tag :tmp_dir @tag :tmp_dir
test "deploys app to Teams from a notebook", %{user: user, node: node, tmp_dir: tmp_dir} do test "deploys app to Teams from a notebook", %{user: user, node: node, tmp_dir: tmp_dir} do
team = connect_to_teams(user, node) team = connect_to_teams(user, node)
deployment_group = build(:deployment_group, name: "BAZ", mode: :online) attrs = params_for(:deployment_group, name: "BAZ", mode: :online)
{:ok, id} = Teams.create_deployment_group(team, deployment_group) {:ok, %{id: id}} = Teams.create_deployment_group(team, attrs)
id = to_string(id)
assert_receive {:deployment_group_created, %{id: ^id}} assert_receive {:deployment_group_created, %{id: ^id}}
# creates the app deployment # creates the app deployment

View file

@ -147,11 +147,10 @@ defmodule Livebook.Factory do
end end
def insert_deployment_group(attrs \\ %{}) do def insert_deployment_group(attrs \\ %{}) do
deployment_group = build(:deployment_group, attrs) attrs = params_for(:deployment_group, attrs)
hub = Livebook.Hubs.fetch_hub!(deployment_group.hub_id) hub = Livebook.Hubs.fetch_hub!(attrs.hub_id)
{:ok, id} = Livebook.Teams.create_deployment_group(hub, deployment_group) {:ok, deployment_group} = Livebook.Teams.create_deployment_group(hub, attrs)
deployment_group
%{deployment_group | id: to_string(id)}
end end
def insert_env_var(factory_name, attrs \\ %{}) do def insert_env_var(factory_name, attrs \\ %{}) do