mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-09-20 10:05:57 +08:00
Pass file or directory as open command (#969)
* caputre directory from params * set file if file parameter is a file * set file param * docs * partial review changes * apply review changes * add tests and rename file to path * formatting * applied feedback + fixed test * Apply suggestions from code review Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
parent
24cdca9c18
commit
be1fce326c
|
@ -388,11 +388,11 @@ defmodule Livebook.Utils do
|
|||
end
|
||||
|
||||
# TODO: On Elixir v1.14, use URI.append_query/2
|
||||
defp append_query(%URI{query: query} = uri, query_to_add) when query in [nil, ""] do
|
||||
def append_query(%URI{query: query} = uri, query_to_add) when query in [nil, ""] do
|
||||
%{uri | query: query_to_add}
|
||||
end
|
||||
|
||||
defp append_query(%URI{} = uri, query) do
|
||||
def append_query(%URI{} = uri, query) do
|
||||
%{uri | query: uri.query <> "&" <> query}
|
||||
end
|
||||
|
||||
|
|
|
@ -26,6 +26,12 @@ defmodule LivebookCLI.Server do
|
|||
* If the open-command is a URL, the notebook at the given URL
|
||||
will be imported
|
||||
|
||||
* If the open-command is a directory, the browser window will point
|
||||
to the home page with the directory selected
|
||||
|
||||
* If the open-command is a notebook file, the browser window will point
|
||||
to the opened notebook
|
||||
|
||||
The open-command runs after the server is started. If a server is
|
||||
already running, the browser window will point to the server
|
||||
currently running.
|
||||
|
@ -48,7 +54,6 @@ defmodule LivebookCLI.Server do
|
|||
--name Set a name for the app distributed node
|
||||
--no-token Disable token authentication, enabled by default
|
||||
If LIVEBOOK_PASSWORD is set, it takes precedence over token auth
|
||||
--open Open browser window pointing to the application
|
||||
-p, --port The port to start the web application on, defaults to 8080
|
||||
--sname Set a short name for the app distributed node
|
||||
|
||||
|
@ -87,7 +92,7 @@ defmodule LivebookCLI.Server do
|
|||
case check_endpoint_availability(base_url) do
|
||||
:livebook_running ->
|
||||
IO.puts("Livebook already running on #{base_url}")
|
||||
open_from_options(base_url, opts, extra_args)
|
||||
open_from_args(base_url, extra_args)
|
||||
|
||||
:taken ->
|
||||
print_error(
|
||||
|
@ -100,7 +105,7 @@ defmodule LivebookCLI.Server do
|
|||
# so it's gonna start listening
|
||||
case Application.ensure_all_started(:livebook) do
|
||||
{:ok, _} ->
|
||||
open_from_options(LivebookWeb.Endpoint.access_url(), opts, extra_args)
|
||||
open_from_args(LivebookWeb.Endpoint.access_url(), extra_args)
|
||||
Process.sleep(:infinity)
|
||||
|
||||
{:error, error} ->
|
||||
|
@ -141,25 +146,42 @@ defmodule LivebookCLI.Server do
|
|||
end
|
||||
end
|
||||
|
||||
defp open_from_options(base_url, opts, []) do
|
||||
if opts[:open] do
|
||||
Livebook.Utils.browser_open(base_url)
|
||||
end
|
||||
defp open_from_args(base_url, []) do
|
||||
Livebook.Utils.browser_open(base_url)
|
||||
end
|
||||
|
||||
defp open_from_options(base_url, _opts, ["new"]) do
|
||||
defp open_from_args(base_url, ["new"]) do
|
||||
base_url
|
||||
|> append_path("/explore/notebooks/new")
|
||||
|> Livebook.Utils.browser_open()
|
||||
end
|
||||
|
||||
defp open_from_options(base_url, _opts, [url]) do
|
||||
base_url
|
||||
|> Livebook.Utils.notebook_import_url(url)
|
||||
|> Livebook.Utils.browser_open()
|
||||
defp open_from_args(base_url, [url_or_file_or_dir]) do
|
||||
url = URI.parse(url_or_file_or_dir)
|
||||
|
||||
cond do
|
||||
url.scheme in ~w(http https file) ->
|
||||
base_url
|
||||
|> Livebook.Utils.notebook_import_url(url_or_file_or_dir)
|
||||
|> Livebook.Utils.browser_open()
|
||||
|
||||
File.regular?(url_or_file_or_dir) ->
|
||||
base_url
|
||||
|> append_path("open")
|
||||
|> update_query(%{"path" => url_or_file_or_dir})
|
||||
|> Livebook.Utils.browser_open()
|
||||
|
||||
File.dir?(url_or_file_or_dir) ->
|
||||
base_url
|
||||
|> update_query(%{"path" => url_or_file_or_dir})
|
||||
|> Livebook.Utils.browser_open()
|
||||
|
||||
true ->
|
||||
Livebook.Utils.browser_open(base_url)
|
||||
end
|
||||
end
|
||||
|
||||
defp open_from_options(_base_url, _opts, _extra_args) do
|
||||
defp open_from_args(_base_url, _extra_args) do
|
||||
print_error(
|
||||
"Too many arguments entered. Ensure only one argument is used to specify the file path and all other arguments are preceded by the relevant switch"
|
||||
)
|
||||
|
@ -171,7 +193,6 @@ defmodule LivebookCLI.Server do
|
|||
default_runtime: :string,
|
||||
ip: :string,
|
||||
name: :string,
|
||||
open: :boolean,
|
||||
port: :integer,
|
||||
home: :string,
|
||||
sname: :string,
|
||||
|
@ -252,6 +273,13 @@ defmodule LivebookCLI.Server do
|
|||
|> URI.to_string()
|
||||
end
|
||||
|
||||
defp update_query(url, params) do
|
||||
url
|
||||
|> URI.parse()
|
||||
|> Livebook.Utils.append_query(URI.encode_query(params))
|
||||
|> URI.to_string()
|
||||
end
|
||||
|
||||
defp print_error(message) do
|
||||
IO.ANSI.format([:red, message]) |> IO.puts()
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ defmodule LivebookWeb.HomeLive do
|
|||
alias Livebook.{Sessions, Session, LiveMarkdown, Notebook, FileSystem}
|
||||
|
||||
@impl true
|
||||
def mount(_params, _session, socket) do
|
||||
def mount(params, _session, socket) do
|
||||
if connected?(socket) do
|
||||
Phoenix.PubSub.subscribe(Livebook.PubSub, "tracker_sessions")
|
||||
end
|
||||
|
@ -20,7 +20,7 @@ defmodule LivebookWeb.HomeLive do
|
|||
socket
|
||||
|> SidebarHelpers.shared_home_handlers()
|
||||
|> assign(
|
||||
file: Livebook.Config.local_filesystem_home(),
|
||||
file: determine_file(params),
|
||||
file_info: %{exists: true, access: :read_write},
|
||||
sessions: sessions,
|
||||
notebook_infos: notebook_infos,
|
||||
|
@ -192,6 +192,18 @@ defmodule LivebookWeb.HomeLive do
|
|||
end
|
||||
end
|
||||
|
||||
def handle_params(%{"path" => path} = _params, _uri, socket)
|
||||
when socket.assigns.live_action == :public_open do
|
||||
file = FileSystem.File.local(path)
|
||||
|
||||
if file_running?(file, socket.assigns.sessions) do
|
||||
session_id = session_id_by_file(file, socket.assigns.sessions)
|
||||
{:noreply, push_redirect(socket, to: Routes.session_path(socket, :page, session_id))}
|
||||
else
|
||||
{:noreply, open_notebook(socket, FileSystem.File.local(path))}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_params(_params, _url, socket), do: {:noreply, socket}
|
||||
|
||||
@impl true
|
||||
|
@ -225,19 +237,7 @@ defmodule LivebookWeb.HomeLive do
|
|||
|
||||
def handle_event("open", %{}, socket) do
|
||||
file = socket.assigns.file
|
||||
|
||||
socket =
|
||||
case import_notebook(file) do
|
||||
{:ok, {notebook, messages}} ->
|
||||
socket
|
||||
|> put_import_warnings(messages)
|
||||
|> create_session(notebook: notebook, file: file, origin: {:file, file})
|
||||
|
||||
{:error, error} ->
|
||||
put_flash(socket, :error, Livebook.Utils.upcase_first(error))
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
{:noreply, open_notebook(socket, file)}
|
||||
end
|
||||
|
||||
def handle_event("bulk_action", %{"action" => "disconnect"} = params, socket) do
|
||||
|
@ -320,8 +320,6 @@ defmodule LivebookWeb.HomeLive do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info(_message, socket), do: {:noreply, socket}
|
||||
|
||||
defp files(sessions) do
|
||||
Enum.map(sessions, & &1.file)
|
||||
end
|
||||
|
@ -384,4 +382,33 @@ defmodule LivebookWeb.HomeLive do
|
|||
defp selected_sessions(sessions, selected_session_ids) do
|
||||
Enum.filter(sessions, &(&1.id in selected_session_ids))
|
||||
end
|
||||
|
||||
defp determine_file(%{"path" => path} = _params) do
|
||||
cond do
|
||||
File.dir?(path) ->
|
||||
path
|
||||
|> FileSystem.Utils.ensure_dir_path()
|
||||
|> FileSystem.File.local()
|
||||
|
||||
File.regular?(path) ->
|
||||
FileSystem.File.local(path)
|
||||
|
||||
true ->
|
||||
Livebook.Config.local_filesystem_home()
|
||||
end
|
||||
end
|
||||
|
||||
defp determine_file(_params), do: Livebook.Config.local_filesystem_home()
|
||||
|
||||
defp open_notebook(socket, file) do
|
||||
case import_notebook(file) do
|
||||
{:ok, {notebook, messages}} ->
|
||||
socket
|
||||
|> put_import_warnings(messages)
|
||||
|> create_session(notebook: notebook, file: file, origin: {:file, file})
|
||||
|
||||
{:error, error} ->
|
||||
put_flash(socket, :error, Livebook.Utils.upcase_first(error))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -78,6 +78,7 @@ defmodule LivebookWeb.Router do
|
|||
pipe_through [:browser, :auth]
|
||||
|
||||
live "/import", HomeLive, :public_import
|
||||
live "/open", HomeLive, :public_open
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -325,6 +325,29 @@ defmodule LivebookWeb.HomeLiveTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "public open endpoint" do
|
||||
test "checkouts the directory when a directory is passed", %{conn: conn} do
|
||||
directory_path = Path.join(File.cwd!(), "test")
|
||||
|
||||
{:ok, view, _} = live(conn, "/?path=#{directory_path}")
|
||||
|
||||
assert render(view) =~ directory_path
|
||||
end
|
||||
|
||||
@tag :tmp_dir
|
||||
test "opens a file when livebook file is passed", %{conn: conn, tmp_dir: tmp_dir} do
|
||||
notebook_path = Path.join(tmp_dir, "notebook.livemd")
|
||||
|
||||
:ok = File.write(notebook_path, "# Notebook OPEN section")
|
||||
|
||||
assert {:error, {:live_redirect, %{flash: %{}, to: to}}} =
|
||||
live(conn, "/open?path=#{notebook_path}")
|
||||
|
||||
{:ok, view, _} = live(conn, to)
|
||||
assert render(view) =~ "Notebook OPEN section"
|
||||
end
|
||||
end
|
||||
|
||||
# Helpers
|
||||
|
||||
defp test_notebook_path(name) do
|
||||
|
|
Loading…
Reference in a new issue