Attempt to load a flat remote URL as a relative path fallback (#1428)

This commit is contained in:
Jonatan Kłosko 2022-09-22 15:11:29 +02:00 committed by GitHub
parent 1799f0a303
commit b20ac022a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 6 deletions

View file

@ -6,7 +6,7 @@ defmodule LivebookWeb.SessionLive do
import Livebook.Utils, only: [format_bytes: 1]
alias Livebook.{Sessions, Session, Delta, Notebook, Runtime, LiveMarkdown}
alias Livebook.Notebook.Cell
alias Livebook.Notebook.{Cell, ContentLoader}
alias Livebook.JSInterop
@impl true
@ -1242,7 +1242,8 @@ defmodule LivebookWeb.SessionLive do
|> redirect_to_self()
resolution_location ->
origin = Notebook.ContentLoader.resolve_location(resolution_location, relative_path)
origin = ContentLoader.resolve_location(resolution_location, relative_path)
fallback_locations = fallback_relative_locations(resolution_location, relative_path)
case session_id_by_location(origin) do
{:ok, session_id} ->
@ -1252,7 +1253,7 @@ defmodule LivebookWeb.SessionLive do
push_redirect(socket, to: redirect_path)
{:error, :none} ->
open_notebook(socket, origin, requested_url)
open_notebook(socket, origin, fallback_locations, requested_url)
{:error, :many} ->
origin_str =
@ -1282,12 +1283,12 @@ defmodule LivebookWeb.SessionLive do
end
end
defp open_notebook(socket, origin, requested_url) do
case Notebook.ContentLoader.fetch_content_from_location(origin) do
defp open_notebook(socket, origin, fallback_locations, requested_url) do
case fetch_content_with_fallbacks(origin, fallback_locations) do
{:ok, content} ->
{notebook, messages} = Livebook.LiveMarkdown.notebook_from_livemd(content)
# If the current session has no path, fork the notebook
# If the current session has no file, fork the notebook
fork? = socket.private.data.file == nil
{file, notebook} = file_and_notebook(fork?, origin, notebook)
url_hash = get_url_hash(requested_url)
@ -1308,6 +1309,32 @@ defmodule LivebookWeb.SessionLive do
end
end
def fallback_relative_locations({:file, _}, _relative_path), do: []
def fallback_relative_locations(resolution_location, relative_path) do
# Other locations to check in case the relative location doesn't
# exist. For example, in ExDoc all pages (including notebooks) are
# flat, regardless of how they are structured in the file system
name = relative_path |> String.split("/") |> Enum.at(-1)
flat_location = ContentLoader.resolve_location(resolution_location, name)
[flat_location]
end
defp fetch_content_with_fallbacks(location, fallbacks) do
case ContentLoader.fetch_content_from_location(location) do
{:ok, content} ->
{:ok, content}
error ->
fallbacks
|> Enum.reject(&(&1 == location))
|> Enum.find_value(error, fn fallback ->
with {:error, _} <- ContentLoader.fetch_content_from_location(fallback), do: nil
end)
end
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)}

View file

@ -834,6 +834,34 @@ defmodule LivebookWeb.SessionLiveTest do
close_session_by_id(session_id)
end
test "when a remote URL cannot be loaded, attempts to resolve a flat URL", %{conn: conn} do
bypass = Bypass.open()
# Multi-level path is not available
Bypass.expect_once(bypass, "GET", "/nested/path/to/notebook.livemd", fn conn ->
Plug.Conn.resp(conn, 500, "Error")
end)
# A flat path is available
Bypass.expect_once(bypass, "GET", "/notebook.livemd", fn conn ->
conn
|> Plug.Conn.put_resp_content_type("text/plain")
|> Plug.Conn.resp(200, "# My notebook")
end)
index_url = url(bypass.port) <> "/index.livemd"
{:ok, session} = Sessions.create_session(origin: {:url, index_url})
assert {:error, {:live_redirect, %{to: "/sessions/" <> session_id}}} =
result = live(conn, "/sessions/#{session.id}/nested/path/to/notebook.livemd")
{:ok, view, _} = follow_redirect(result, conn)
assert render(view) =~ "My notebook"
Session.close(session.pid)
close_session_by_id(session_id)
end
test "renders an error message if relative remote notebook cannot be loaded", %{conn: conn} do
bypass = Bypass.open()