Skip to content

Commit

Permalink
(tmp) Load requested or default tenant in DeleteShapePlug
Browse files Browse the repository at this point in the history
  • Loading branch information
alco committed Oct 17, 2024
1 parent 747e789 commit f0b3196
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 14 deletions.
56 changes: 56 additions & 0 deletions packages/sync-service/lib/electric/plug/delete_shape_plug.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ defmodule Electric.Plug.DeleteShapePlug do

alias Electric.Shapes
alias Electric.Plug.ServeShapePlug.Params
alias Electric.TenantManager

plug :fetch_query_params
plug :put_resp_content_type, "application/json"

plug :allow_shape_deletion
plug :validate_tenant_id
plug :load_tenant
plug :validate_query_params

plug :truncate_or_delete_shape
Expand All @@ -23,6 +26,59 @@ defmodule Electric.Plug.DeleteShapePlug do
end
end

defp validate_tenant_id(%Plug.Conn{} = conn, _) do
case Map.get(conn.query_params, "database_id", :not_found) do
:not_found ->
conn

id when is_binary(id) ->
assign(conn, :database_id, id)

_ ->
conn
|> send_resp(400, Jason.encode_to_iodata!("database_id should be a connection string"))
|> halt()
end
end

defp load_tenant(%Plug.Conn{assigns: %{database_id: tenant_id}} = conn, _) do
{:ok, tenant_config} = TenantManager.get_tenant(tenant_id, conn.assigns.config)
assign_tenant(conn, tenant_config)
end

defp load_tenant(%Plug.Conn{} = conn, _) do
# Tenant ID is not specified
# ask the tenant manager for the only tenant
# if there's more than one tenant we reply with an error
case TenantManager.get_only_tenant(conn.assigns.config) do
{:ok, tenant_config} ->
assign_tenant(conn, tenant_config)

{:error, :not_found} ->
conn
|> send_resp(400, Jason.encode_to_iodata!("No database found"))
|> halt()

{:error, :several_tenants} ->
conn
|> send_resp(
400,
Jason.encode_to_iodata!(
"Database ID was not provided and there are multiple databases. Please specify a database ID using the `database_id` query parameter."
)
)
|> halt()
end
end

defp assign_tenant(%Plug.Conn{} = conn, tenant_config) do
id = tenant_config[:tenant_id]

conn
|> assign(:config, tenant_config)
|> assign(:tenant_id, id)
end

defp validate_query_params(%Plug.Conn{} = conn, _) do
all_params =
Map.merge(conn.query_params, conn.path_params)
Expand Down
52 changes: 38 additions & 14 deletions packages/sync-service/test/electric/plug/delete_shape_plug_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ defmodule Electric.Plug.DeleteShapePlugTest do

alias Electric.Plug.DeleteShapePlug
alias Electric.Shapes.Shape
alias Electric.TenantManager

import Support.ComponentSetup
import Support.TestUtils, only: [with_electric_instance_id: 1]

alias Support.Mock

Expand All @@ -25,6 +29,7 @@ defmodule Electric.Plug.DeleteShapePlugTest do
}
}
@test_shape_id "test-shape-id"
@test_pg_id "12345"

def load_column_info({"public", "users"}, _),
do: {:ok, @test_shape.table_info[{"public", "users"}][:columns]}
Expand All @@ -37,26 +42,42 @@ defmodule Electric.Plug.DeleteShapePlugTest do
:ok
end

def conn(method, "?" <> _ = query_string, allow \\ true) do
def conn(ctx, method, "?" <> _ = query_string, allow \\ true) do
# Pass mock dependencies to the plug
config = %{
tenant = [
electric_instance_id: ctx.electric_instance_id,
tenant_id: ctx.tenant_id,
pg_id: @test_pg_id,
shape_cache: {Mock.ShapeCache, []},
storage: {Mock.Storage, []},
inspector: {__MODULE__, []},
registry: @registry,
long_poll_timeout: 20_000,
max_age: 60,
stale_age: 300,
long_poll_timeout: Access.get(ctx, :long_poll_timeout, 20_000),
max_age: Access.get(ctx, :max_age, 60),
stale_age: Access.get(ctx, :stale_age, 300)
]

# because test mode creates a tenant by default
TenantManager.delete_tenant(ctx.tenant_id)
:ok = TenantManager.store_tenant(tenant)

config = [
storage: {Mock.Storage, []},
tenant_manager: Electric.TenantManager,
allow_shape_deletion: allow
}
]

Plug.Test.conn(method, "/" <> query_string)
|> assign(:config, config)
end

describe "DeleteShapePlug" do
test "returns 404 if shape deletion is not allowed" do
setup [:with_electric_instance_id, :with_tenant_id]

test "returns 404 if shape deletion is not allowed", ctx do
conn =
conn("DELETE", "?root_table=.invalid_shape", false)
ctx
|> conn("DELETE", "?root_table=.invalid_shape", false)
|> DeleteShapePlug.call([])

assert conn.status == 404
Expand All @@ -66,9 +87,10 @@ defmodule Electric.Plug.DeleteShapePlugTest do
}
end

test "returns 400 for invalid params" do
test "returns 400 for invalid params", ctx do
conn =
conn("DELETE", "?root_table=.invalid_shape")
ctx
|> conn("DELETE", "?root_table=.invalid_shape")
|> DeleteShapePlug.call([])

assert conn.status == 400
Expand All @@ -80,24 +102,26 @@ defmodule Electric.Plug.DeleteShapePlugTest do
}
end

test "should clean shape based on shape definition" do
test "should clean shape based on shape definition", ctx do
Mock.ShapeCache
|> expect(:get_or_create_shape_id, fn @test_shape, _opts -> {@test_shape_id, 0} end)
|> expect(:clean_shape, fn @test_shape_id, _ -> :ok end)

conn =
conn(:delete, "?root_table=public.users")
ctx
|> conn(:delete, "?root_table=public.users")
|> DeleteShapePlug.call([])

assert conn.status == 202
end

test "should clean shape based on shape_id" do
test "should clean shape based on shape_id", ctx do
Mock.ShapeCache
|> expect(:clean_shape, fn @test_shape_id, _ -> :ok end)

conn =
conn(:delete, "?root_table=public.users&shape_id=#{@test_shape_id}")
ctx
|> conn(:delete, "?root_table=public.users&shape_id=#{@test_shape_id}")
|> DeleteShapePlug.call([])

assert conn.status == 202
Expand Down

0 comments on commit f0b3196

Please sign in to comment.