Skip to content

Commit

Permalink
Support file scheme when importing from URL (#706)
Browse files Browse the repository at this point in the history
* Add test

* Support file scheme when importing from URL
  • Loading branch information
jonatanklosko authored Nov 12, 2021
1 parent d78a3cf commit 4d92aeb
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 30 deletions.
52 changes: 52 additions & 0 deletions lib/livebook/content_loader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ defmodule Livebook.ContentLoader do

alias Livebook.Utils.HTTP

@typedoc """
A location from where content gets loaded.
"""
@type location :: {:file, FileSystem.File.t()} | {:url, String.t()}

@doc """
Rewrite known URLs, so that they point to plain text file rather than HTML.
Expand Down Expand Up @@ -81,4 +86,51 @@ defmodule Livebook.ContentLoader do
{:error, "failed to download notebook from the given URL"}
end
end

@doc """
Loads a notebook content from the given location.
"""
@spec fetch_content_from_location(location()) :: {:ok, String.t()} | {:error, String.t()}
def fetch_content_from_location(location)

def fetch_content_from_location({:file, file}) do
case Livebook.FileSystem.File.read(file) do
{:ok, content} -> {:ok, content}
{:error, message} -> {:error, "failed to read #{file.path}, reason: #{message}"}
end
end

def fetch_content_from_location({:url, url}) do
url
|> rewrite_url()
|> fetch_content()
end

@doc """
Normalizes the given URL into a location.
"""
@spec url_to_location(String.t()) :: location()
def url_to_location(url)

def url_to_location("file://" <> path) do
path = Path.expand(path)
file = Livebook.FileSystem.File.local(path)
{:file, file}
end

def url_to_location(url), do: {:url, url}

@doc """
Resolves the given relative path with regard to the given location.
"""
@spec resolve_location(location(), String.t()) :: location()
def resolve_location(location, relative_path)

def resolve_location({:url, url}, relative_path) do
{:url, Livebook.Utils.expand_url(url, relative_path)}
end

def resolve_location({:file, file}, relative_path) do
{:file, Livebook.FileSystem.File.resolve(file, relative_path)}
end
end
2 changes: 1 addition & 1 deletion lib/livebook/session.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ defmodule Livebook.Session do
@type t :: %__MODULE__{
id: id(),
pid: pid(),
origin: {:file, FileSystem.File.t()} | {:url, String.t()} | nil,
origin: Livebook.ContentLoader.location() | nil,
notebook_name: String.t(),
file: FileSystem.File.t() | nil,
images_dir: FileSystem.File.t(),
Expand Down
9 changes: 5 additions & 4 deletions lib/livebook_web/live/home_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,13 @@ defmodule LivebookWeb.HomeLive do
end

def handle_params(%{"url" => url}, _url, %{assigns: %{live_action: :public_import}} = socket) do
url
|> Livebook.ContentLoader.rewrite_url()
|> Livebook.ContentLoader.fetch_content()
origin = Livebook.ContentLoader.url_to_location(url)

origin
|> Livebook.ContentLoader.fetch_content_from_location()
|> case do
{:ok, content} ->
socket = import_content(socket, content, origin: {:url, url})
socket = import_content(socket, content, origin: origin)
{:noreply, socket}

{:error, _message} ->
Expand Down
11 changes: 6 additions & 5 deletions lib/livebook_web/live/home_live/import_url_component.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule LivebookWeb.HomeLive.ImportUrlComponent do
use LivebookWeb, :live_component

alias Livebook.{ContentLoader, Utils}
alias Livebook.Utils

@impl true
def mount(socket) do
Expand Down Expand Up @@ -58,12 +58,13 @@ defmodule LivebookWeb.HomeLive.ImportUrlComponent do
end

defp do_import(socket, url) do
url
|> ContentLoader.rewrite_url()
|> ContentLoader.fetch_content()
origin = Livebook.ContentLoader.url_to_location(url)

origin
|> Livebook.ContentLoader.fetch_content_from_location()
|> case do
{:ok, content} ->
send(self(), {:import_content, content, [origin: {:url, url}]})
send(self(), {:import_content, content, [origin: origin]})
socket

{:error, message} ->
Expand Down
23 changes: 3 additions & 20 deletions lib/livebook_web/live/session_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ defmodule LivebookWeb.SessionLive do
import Livebook.Utils, only: [access_by_id: 1]

alias LivebookWeb.SidebarHelpers
alias Livebook.{Sessions, Session, Delta, Notebook, Runtime, LiveMarkdown, FileSystem}
alias Livebook.{Sessions, Session, Delta, Notebook, Runtime, LiveMarkdown}
alias Livebook.Notebook.Cell
alias Livebook.JSInterop

Expand Down Expand Up @@ -961,11 +961,7 @@ defmodule LivebookWeb.SessionLive do
|> redirect_to_self()

resolution_location ->
origin =
case resolution_location do
{:url, url} -> {:url, Livebook.Utils.expand_url(url, relative_path)}
{:file, file} -> {:file, FileSystem.File.resolve(file, relative_path)}
end
origin = Livebook.ContentLoader.resolve_location(resolution_location, relative_path)

case session_id_by_location(origin) do
{:ok, session_id} ->
Expand Down Expand Up @@ -996,7 +992,7 @@ defmodule LivebookWeb.SessionLive do
defp location(%{origin: origin}), do: origin

defp open_notebook(socket, origin) do
case load_content(origin) do
case Livebook.ContentLoader.fetch_content_from_location(origin) do
{:ok, content} ->
{notebook, messages} = Livebook.LiveMarkdown.Import.notebook_from_markdown(content)

Expand All @@ -1015,19 +1011,6 @@ defmodule LivebookWeb.SessionLive do
end
end

defp load_content({:file, file}) do
case FileSystem.File.read(file) do
{:ok, content} -> {:ok, content}
{:error, message} -> {:error, "failed to read #{file.path}, reason: #{message}"}
end
end

defp load_content({:url, url}) do
url
|> Livebook.ContentLoader.rewrite_url()
|> Livebook.ContentLoader.fetch_content()
end

defp file_and_notebook(fork?, origin, notebook)
defp file_and_notebook(false, {:file, file}, notebook), do: {file, notebook}
defp file_and_notebook(true, {:file, _file}, notebook), do: {nil, Notebook.forked(notebook)}
Expand Down
13 changes: 13 additions & 0 deletions test/livebook_web/live/home_live_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,19 @@ defmodule LivebookWeb.HomeLiveTest do
assert render(view) =~ "My notebook"
end

@tag :tmp_dir
test "imports notebook from local file URL", %{conn: conn, tmp_dir: tmp_dir} do
notebook_path = Path.join(tmp_dir, "notebook.livemd")
File.write!(notebook_path, "# My notebook")
notebook_url = "file://" <> notebook_path

assert {:error, {:live_redirect, %{to: to}}} =
live(conn, "/import?url=#{URI.encode_www_form(notebook_url)}")

{:ok, view, _} = live(conn, to)
assert render(view) =~ "My notebook"
end

test "redirects to the import form on error", %{conn: conn} do
bypass = Bypass.open()

Expand Down

0 comments on commit 4d92aeb

Please sign in to comment.