Save notebook and notify clients when session is stopped (#45)

This commit is contained in:
Jonatan Kłosko 2021-02-21 19:17:14 +01:00 committed by GitHub
parent 0925ec77cd
commit c1eafc9a2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 6 deletions

View file

@ -215,11 +215,14 @@ defmodule LiveBook.Session do
end end
@doc """ @doc """
Synchronously stops the server. Asynchronously sends a close request to the server.
This results in saving the file and broadcasting
a :closed message to the session topic.
""" """
@spec stop(id()) :: :ok @spec close(id()) :: :ok
def stop(session_id) do def close(session_id) do
GenServer.stop(name(session_id)) GenServer.cast(name(session_id), :close)
end end
## Callbacks ## Callbacks
@ -377,6 +380,13 @@ defmodule LiveBook.Session do
{:noreply, maybe_save_notebook(state)} {:noreply, maybe_save_notebook(state)}
end end
def handle_cast(:close, state) do
maybe_save_notebook(state)
broadcast_message(state.session_id, :session_closed)
{:stop, :shutdown, state}
end
@impl true @impl true
def handle_info({:DOWN, ref, :process, _, _}, %{runtime_monitor_ref: ref} = state) do def handle_info({:DOWN, ref, :process, _, _}, %{runtime_monitor_ref: ref} = state) do
broadcast_info(state.session_id, "runtime node terminated unexpectedly") broadcast_info(state.session_id, "runtime node terminated unexpectedly")

View file

@ -50,13 +50,13 @@ defmodule LiveBook.SessionSupervisor do
end end
@doc """ @doc """
Synchronously stops a session process identified by the given id. Asynchronously stops a session process identified by the given id.
Broadcasts `{:session_delete, id}` message under the `"sessions"` topic. Broadcasts `{:session_delete, id}` message under the `"sessions"` topic.
""" """
@spec delete_session(Session.id()) :: :ok @spec delete_session(Session.id()) :: :ok
def delete_session(id) do def delete_session(id) do
Session.stop(id) Session.close(id)
broadcast_sessions_message({:session_deleted, id}) broadcast_sessions_message({:session_deleted, id})
:ok :ok
end end

View file

@ -382,6 +382,13 @@ defmodule LiveBookWeb.SessionLive do
{:noreply, put_flash(socket, :info, message)} {:noreply, put_flash(socket, :info, message)}
end end
def handle_info(:session_closed, socket) do
{:noreply,
socket
|> put_flash(:info, "Session has been closed")
|> push_redirect(to: Routes.home_path(socket, :page))}
end
def handle_info(_message, socket), do: {:noreply, socket} def handle_info(_message, socket), do: {:noreply, socket}
defp after_operation(socket, _prev_socket, {:insert_section, _index, section_id}) do defp after_operation(socket, _prev_socket, {:insert_section, _index, section_id}) do

View file

@ -23,9 +23,11 @@ defmodule LiveBook.SessionSupervisorTest do
test "stops the session process identified by the given id" do test "stops the session process identified by the given id" do
{:ok, id} = SessionSupervisor.create_session() {:ok, id} = SessionSupervisor.create_session()
{:ok, pid} = SessionSupervisor.get_session_pid(id) {:ok, pid} = SessionSupervisor.get_session_pid(id)
ref = Process.monitor(pid)
SessionSupervisor.delete_session(id) SessionSupervisor.delete_session(id)
assert_receive {:DOWN, ^ref, :process, _, _}
refute has_child_with_pid?(SessionSupervisor, pid) refute has_child_with_pid?(SessionSupervisor, pid)
end end

View file

@ -187,6 +187,28 @@ defmodule LiveBook.SessionTest do
end end
end end
describe "close/1" do
@tag :tmp_dir
test "saves the notebook and notifies subscribers once the session is closed",
%{session_id: session_id, tmp_dir: tmp_dir} do
path = Path.join(tmp_dir, "notebook.livemd")
Session.set_path(session_id, path)
# Perform a change, so the notebook is dirty
Session.set_notebook_name(session_id, "My notebook")
Phoenix.PubSub.subscribe(LiveBook.PubSub, "sessions:#{session_id}")
refute File.exists?(path)
Process.flag(:trap_exit, true)
Session.close(session_id)
assert_receive :session_closed
assert File.exists?(path)
assert File.read!(path) =~ "My notebook"
end
end
describe "start_link/1" do describe "start_link/1" do
@tag :tmp_dir @tag :tmp_dir
test "fails if the given path is already in use", %{tmp_dir: tmp_dir} do test "fails if the given path is already in use", %{tmp_dir: tmp_dir} do