diff --git a/lib/livebook/utils.ex b/lib/livebook/utils.ex index 0376e9d8c..cd119626b 100644 --- a/lib/livebook/utils.ex +++ b/lib/livebook/utils.ex @@ -185,6 +185,20 @@ defmodule Livebook.Utils do end) end + @doc """ + Validates a change is not an S3 URL. + """ + @spec validate_not_s3_url(Ecto.Changeset.t(), atom(), String.t()) :: Ecto.Changeset.t() + def validate_not_s3_url(changeset, field, message) do + Ecto.Changeset.validate_change(changeset, field, fn ^field, url -> + if valid_url?(url) and String.starts_with?(url, "s3://") do + [{field, message}] + else + [] + end + end) + end + @doc ~S""" Validates if the given string forms valid CLI flags. diff --git a/lib/livebook_web/live/file_select_component.ex b/lib/livebook_web/live/file_select_component.ex index 19f0f4101..68b5e3225 100644 --- a/lib/livebook_web/live/file_select_component.ex +++ b/lib/livebook_web/live/file_select_component.ex @@ -268,11 +268,14 @@ defmodule LivebookWeb.FileSelectComponent do <:toggle> <%= for file_system <- @file_systems do %> diff --git a/lib/livebook_web/live/file_system_helpers.ex b/lib/livebook_web/live/file_system_helpers.ex index 5ad29859f..7617fa917 100644 --- a/lib/livebook_web/live/file_system_helpers.ex +++ b/lib/livebook_web/live/file_system_helpers.ex @@ -3,12 +3,20 @@ defmodule LivebookWeb.FileSystemHelpers do alias Livebook.FileSystem + @doc """ + Formats the given file system into a short name. + """ + def file_system_name(file_system) + + def file_system_name(%FileSystem.Local{}), do: "Disk" + def file_system_name(%FileSystem.S3{}), do: "S3" + @doc """ Formats the given file system into a descriptive label. """ def file_system_label(file_system) - def file_system_label(%FileSystem.Local{}), do: "Local disk" + def file_system_label(%FileSystem.Local{}), do: "Disk" def file_system_label(%FileSystem.S3{} = fs), do: fs.bucket_url @doc """ diff --git a/lib/livebook_web/live/home_live.ex b/lib/livebook_web/live/home_live.ex index 166cf72a1..e9f154415 100644 --- a/lib/livebook_web/live/home_live.ex +++ b/lib/livebook_web/live/home_live.ex @@ -45,7 +45,10 @@ defmodule LivebookWeb.HomeLive do > <:topbar_action>
- <.link navigate={~p"/open/file"} class="button-base button-outlined-gray whitespace-nowrap"> + <.link + navigate={~p"/open/storage"} + class="button-base button-outlined-gray whitespace-nowrap" + > Open <.link class="button-base button-blue" patch={~p"/new"}> @@ -63,7 +66,7 @@ defmodule LivebookWeb.HomeLive do
- <.link patch={~p"/open/file"} class={["tab", @tab == "file" && "active"]}> + <.link patch={~p"/open/storage"} class={["tab", @tab == "storage" && "active"]}> <.remix_icon icon="file-3-line" class="align-middle" /> - From file + From storage <.link patch={~p"/open/url"} class={["tab", @tab == "url" && "active"]}> <.remix_icon icon="download-cloud-2-line" class="align-middle" /> @@ -72,7 +72,7 @@ defmodule LivebookWeb.OpenLive do
<.live_component - :if={@tab == "file"} + :if={@tab == "storage"} module={LivebookWeb.OpenLive.FileComponent} id="import-file" sessions={@sessions} @@ -129,7 +129,7 @@ defmodule LivebookWeb.OpenLive do
Looking for unsaved notebooks? <.link class="font-semibold" - navigate={~p"/open/file?autosave=true"} + navigate={~p"/open/storage?autosave=true"} phx-no-format >Browse them here.
@@ -165,7 +165,7 @@ defmodule LivebookWeb.OpenLive do expanded_path = Path.expand(path) if File.dir?(expanded_path) do - {:noreply, push_patch(socket, to: ~p"/open/file?path=#{path}")} + {:noreply, push_patch(socket, to: ~p"/open/storage?path=#{path}")} else file = FileSystem.File.local(expanded_path) diff --git a/lib/livebook_web/live/open_live/url_component.ex b/lib/livebook_web/live/open_live/url_component.ex index 654fcf3a9..a3e8b0a87 100644 --- a/lib/livebook_web/live/open_live/url_component.ex +++ b/lib/livebook_web/live/open_live/url_component.ex @@ -17,6 +17,10 @@ defmodule LivebookWeb.OpenLive.UrlComponent do cast({data, types}, attrs, [:url]) |> validate_required([:url]) |> Livebook.Utils.validate_url(:url) + |> Livebook.Utils.validate_not_s3_url( + :url, + ~s{invalid s3:// URL scheme, you must first connect to the Cloud Storage in your Hub page and then choose the relevant file in "From storage"} + ) end @impl true diff --git a/lib/livebook_web/live/session_live.ex b/lib/livebook_web/live/session_live.ex index 383068dbb..1091bdbf3 100644 --- a/lib/livebook_web/live/session_live.ex +++ b/lib/livebook_web/live/session_live.ex @@ -888,11 +888,11 @@ defmodule LivebookWeb.SessionLive do
<.link - patch={~p"/sessions/#{@session.id}/add-file/file"} - class={["tab", @tab == "file" && "active"]} + patch={~p"/sessions/#{@session.id}/add-file/storage"} + class={["tab", @tab == "storage" && "active"]} > <.remix_icon icon="file-3-line" class="align-middle" /> - From file + From storage <.link patch={~p"/sessions/#{@session.id}/add-file/url"} @@ -918,7 +918,7 @@ defmodule LivebookWeb.SessionLive do
<.live_component - :if={@tab == "file"} + :if={@tab == "storage"} module={LivebookWeb.SessionLive.AddFileEntryFileComponent} id="add-file-entry-from-file" session={@session} diff --git a/lib/livebook_web/live/session_live/add_file_entry_url_component.ex b/lib/livebook_web/live/session_live/add_file_entry_url_component.ex index a954b6856..4e12de754 100644 --- a/lib/livebook_web/live/session_live/add_file_entry_url_component.ex +++ b/lib/livebook_web/live/session_live/add_file_entry_url_component.ex @@ -33,6 +33,10 @@ defmodule LivebookWeb.SessionLive.AddFileEntryUrlComponent do |> validate_required([:url, :name, :copy]) |> Livebook.Notebook.validate_file_entry_name(:name) |> Livebook.Utils.validate_url(:url) + |> Livebook.Utils.validate_not_s3_url( + :url, + ~s{invalid s3:// URL scheme, you must first connect to the Cloud Storage in your Hub page and then choose the relevant file in "From storage"} + ) end @impl true diff --git a/lib/livebook_web/live/session_live/files_list_component.ex b/lib/livebook_web/live/session_live/files_list_component.ex index db2def26b..42505e4a2 100644 --- a/lib/livebook_web/live/session_live/files_list_component.ex +++ b/lib/livebook_web/live/session_live/files_list_component.ex @@ -135,7 +135,7 @@ defmodule LivebookWeb.SessionLive.FilesListComponent do <.link class="w-full flex items-center justify-center p-8 py-1 space-x-2 text-sm font-medium text-gray-500 border border-gray-400 border-dashed rounded-xl hover:bg-gray-100" role="button" - patch={~p"/sessions/#{@session.id}/add-file/file"} + patch={~p"/sessions/#{@session.id}/add-file/storage"} > <.remix_icon icon="add-line" class="text-lg align-center" /> New file diff --git a/test/livebook_web/live/open_live_test.exs b/test/livebook_web/live/open_live_test.exs index 5a5ae70cc..569f8d4c8 100644 --- a/test/livebook_web/live/open_live_test.exs +++ b/test/livebook_web/live/open_live_test.exs @@ -7,7 +7,7 @@ defmodule LivebookWeb.OpenLiveTest do describe "file selection" do test "updates the list of files as the input changes", %{conn: conn} do - {:ok, view, _} = live(conn, ~p"/open/file") + {:ok, view, _} = live(conn, ~p"/open/storage") path = Path.expand("../../../lib", __DIR__) <> "/" @@ -20,7 +20,7 @@ defmodule LivebookWeb.OpenLiveTest do end test "allows importing when a notebook file is selected", %{conn: conn} do - {:ok, view, _} = live(conn, ~p"/open/file") + {:ok, view, _} = live(conn, ~p"/open/storage") path = test_notebook_path("basic") @@ -45,7 +45,7 @@ defmodule LivebookWeb.OpenLiveTest do @tag :tmp_dir test "disables import when a directory is selected", %{conn: conn, tmp_dir: tmp_dir} do - {:ok, view, _} = live(conn, ~p"/open/file") + {:ok, view, _} = live(conn, ~p"/open/storage") view |> element(~s{form[phx-change="set_path"]}) @@ -57,7 +57,7 @@ defmodule LivebookWeb.OpenLiveTest do end test "disables import when a nonexistent file is selected", %{conn: conn} do - {:ok, view, _} = live(conn, ~p"/open/file") + {:ok, view, _} = live(conn, ~p"/open/storage") path = File.cwd!() |> Path.join("nonexistent.livemd") @@ -73,7 +73,7 @@ defmodule LivebookWeb.OpenLiveTest do @tag :tmp_dir test "disables open when a write-protected notebook is selected", %{conn: conn, tmp_dir: tmp_dir} do - {:ok, view, _} = live(conn, ~p"/open/file") + {:ok, view, _} = live(conn, ~p"/open/storage") path = Path.join(tmp_dir, "write_protected.livemd") File.touch!(path) @@ -284,7 +284,7 @@ defmodule LivebookWeb.OpenLiveTest do assert {:error, {:live_redirect, %{to: to}}} = live(conn, ~p"/open?path=#{directory_path}") - assert to == ~p"/open/file?path=#{directory_path}" + assert to == ~p"/open/storage?path=#{directory_path}" {:ok, view, _} = live(conn, to) assert render(view) =~ directory_path diff --git a/test/livebook_web/live/session_live_test.exs b/test/livebook_web/live/session_live_test.exs index 7b7bd2937..ddabb0791 100644 --- a/test/livebook_web/live/session_live_test.exs +++ b/test/livebook_web/live/session_live_test.exs @@ -1688,14 +1688,14 @@ defmodule LivebookWeb.SessionLiveTest do describe "file management" do @tag :tmp_dir - test "adding :attachment file entry from file", + test "adding :attachment file entry from storage", %{conn: conn, session: session, tmp_dir: tmp_dir} do Session.subscribe(session.id) path = Path.join(tmp_dir, "image.jpg") File.write!(path, "content") - {:ok, view, _} = live(conn, ~p"/sessions/#{session.id}/add-file/file") + {:ok, view, _} = live(conn, ~p"/sessions/#{session.id}/add-file/storage") view |> element(~s{form[phx-change="set_path"]}) @@ -1721,11 +1721,11 @@ defmodule LivebookWeb.SessionLiveTest do end @tag :tmp_dir - test "adding :file file entry from file", %{conn: conn, session: session, tmp_dir: tmp_dir} do + test "adding :file file entry from storage", %{conn: conn, session: session, tmp_dir: tmp_dir} do path = Path.join(tmp_dir, "image.jpg") File.write!(path, "content") - {:ok, view, _} = live(conn, ~p"/sessions/#{session.id}/add-file/file") + {:ok, view, _} = live(conn, ~p"/sessions/#{session.id}/add-file/storage") view |> element(~s{form[phx-change="set_path"]})