Skip to content

Commit

Permalink
Support multiple repos on sandbox plug API (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
Cowa committed Nov 5, 2022
1 parent 613614b commit 3ff1818
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 17 deletions.
12 changes: 6 additions & 6 deletions lib/phoenix_ecto/sql/sandbox.ex
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,12 @@ defmodule Phoenix.Ecto.SQL.Sandbox do
iex> {:ok, _owner_pid, metadata} = start_child(MyApp.Repo)
"""
def start_child(repo, opts \\ []) do
child_spec = {SandboxSession, {repo, self(), opts}}
def start_child(repos, opts \\ []) do
child_spec = {SandboxSession, {repos, self(), opts}}

case DynamicSupervisor.start_child(SandboxSupervisor, child_spec) do
{:ok, owner} ->
metadata = metadata_for(repo, owner)
metadata = metadata_for(repos, owner)
{:ok, owner, metadata}

{:error, reason} ->
Expand All @@ -194,7 +194,7 @@ defmodule Phoenix.Ecto.SQL.Sandbox do
%{
header: Keyword.get(opts, :header, "user-agent"),
path: get_path_info(opts[:at]),
repo: opts[:repo],
repos: List.wrap(opts[:repo]),
sandbox: session_opts[:sandbox] || Ecto.Adapters.SQL.Sandbox,
session_opts: session_opts
}
Expand All @@ -205,8 +205,8 @@ defmodule Phoenix.Ecto.SQL.Sandbox do

@doc false
def call(%Conn{method: "POST", path_info: path} = conn, %{path: path} = opts) do
%{repo: repo, session_opts: session_opts} = opts
{:ok, _owner, metadata} = start_child(repo, session_opts)
%{repos: repos, session_opts: session_opts} = opts
{:ok, _owner, metadata} = start_child(repos, session_opts)

conn
|> put_resp_content_type("text/plain")
Expand Down
30 changes: 19 additions & 11 deletions lib/phoenix_ecto/sql/sandbox_session.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,27 @@ defmodule Phoenix.Ecto.SQL.SandboxSession do

@timeout 15_000

def start_link({repo, client, opts}) do
GenServer.start_link(__MODULE__, [repo, client, opts])
def start_link({repos, client, opts}) do
GenServer.start_link(__MODULE__, [repos, client, opts])
end

def init([repo, client, opts]) do
def init([repos, client, opts]) do
timeout = opts[:timeout] || @timeout
sandbox = opts[:sandbox] || Ecto.Adapters.SQL.Sandbox

:ok = checkout_connection(sandbox, repo, client)
:ok = checkout_connection(sandbox, repos, client)
Process.send_after(self(), :timeout, timeout)

{:ok, %{repo: repo, client: client, sandbox: sandbox}}
{:ok, %{repos: repos, client: client, sandbox: sandbox}}
end

def handle_call(:checkin, _from, state) do
:ok = checkin_connection(state.sandbox, state.repo, state.client)
:ok = checkin_connection(state.sandbox, state.repos, state.client)
{:stop, :shutdown, :ok, state}
end

def handle_info(:timeout, state) do
:ok = checkin_connection(state.sandbox, state.repo, state.client)
:ok = checkin_connection(state.sandbox, state.repos, state.client)
{:stop, :shutdown, state}
end

Expand All @@ -33,11 +33,19 @@ defmodule Phoenix.Ecto.SQL.SandboxSession do
{:noreply, state}
end

defp checkin_connection(sandbox, repo, client) do
sandbox.checkin(repo, client: client)
defp checkin_connection(sandbox, repos, client) do
for repo <- repos do
:ok = sandbox.checkin(repo, client: client)
end

:ok
end

defp checkout_connection(sandbox, repo, client) do
sandbox.checkout(repo, client: client)
defp checkout_connection(sandbox, repos, client) do
for repo <- repos do
:ok = sandbox.checkout(repo, client: client)
end

:ok
end
end
44 changes: 44 additions & 0 deletions test/phoenix_ecto/sql/sandbox_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,48 @@ defmodule PhoenixEcto.SQL.SandboxTest do

refute_receive {:allowed, MyRepo}
end

test "checks out/in connection through sandbox owner at path with multiple repos" do
# start new sandbox owner
conn = call_plug_with_checkout(conn(:post, "/sandbox"), repo: [MyRepoOne, MyRepoTwo])
assert "BeamMetadata" <> _ = user_agent = conn.resp_body
assert conn.halted
assert conn.status == 200
assert_receive {:checkout, MyRepoOne}
assert_receive {:checkout, MyRepoTwo}

# no allow with missing header
conn = call_plug_with_checkout(conn(:get, "/"), repo: [MyRepoOne, MyRepoTwo])
refute conn.halted
refute_receive {:allowed, MyRepoOne}
refute_receive {:allowed, MyRepoTwo}

# allows new request with metadata in header
conn =
conn(:get, "/")
|> put_req_header("user-agent", user_agent)
|> call_plug_with_checkout(repo: [MyRepoOne, MyRepoTwo])

refute conn.halted
assert_receive {:allowed, MyRepoOne}
assert_receive {:allowed, MyRepoTwo}

# checks in request with metadata
conn =
conn(:delete, "/sandbox")
|> put_req_header("user-agent", user_agent)
|> call_plug_with_checkout(repo: [MyRepoOne, MyRepoTwo])

assert conn.status == 200
assert conn.halted

# old user agent refuses owner who has been checked in
_conn =
conn(:get, "/")
|> put_req_header("user-agent", user_agent)
|> call_plug_with_checkout(repo: [MyRepoOne, MyRepoTwo])

refute_receive {:allowed, MyRepoOne}
refute_receive {:allowed, MyRepoTwo}
end
end

0 comments on commit 3ff1818

Please sign in to comment.