diff --git a/CHANGELOG.md b/CHANGELOG.md index d3a98f2..a9a7252 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.6.2] - 2025-11-05 + +### Changed + +- Bump allowed `electric` to version `1.2.2` [#103](https://github.com/electric-sql/phoenix_sync/pull/116) + ## [0.6.1] - 2025-10-13 ### Fixed diff --git a/lib/phoenix/sync/electric.ex b/lib/phoenix/sync/electric.ex index 40693c2..ef41b61 100644 --- a/lib/phoenix/sync/electric.ex +++ b/lib/phoenix/sync/electric.ex @@ -639,78 +639,3 @@ defmodule Phoenix.Sync.Electric do end end end - -if Code.ensure_loaded?(Electric.Shapes.Api) && - Code.ensure_loaded?(Phoenix.Sync.Electric.ApiAdapter) do - defimpl Phoenix.Sync.Adapter.PlugApi, for: Electric.Shapes.Api do - alias Electric.Shapes - - alias Phoenix.Sync.PredefinedShape - alias Phoenix.Sync.Electric.ApiAdapter - - def predefined_shape(api, %PredefinedShape{} = shape) do - ApiAdapter.new(api, shape) - end - - def call(api, %{method: "GET"} = conn, params) do - case Shapes.Api.validate(api, params) do - {:ok, request} -> - conn - |> content_type() - |> Plug.Conn.assign(:request, request) - |> Shapes.Api.serve_shape_log(request) - - {:error, response} -> - conn - |> content_type() - |> Shapes.Api.Response.send(response) - |> Plug.Conn.halt() - end - end - - def call(api, %{method: "DELETE"} = conn, params) do - case Shapes.Api.validate_for_delete(api, params) do - {:ok, request} -> - conn - |> content_type() - |> Plug.Conn.assign(:request, request) - |> Shapes.Api.delete_shape(request) - - {:error, response} -> - conn - |> content_type() - |> Shapes.Api.Response.send(response) - |> Plug.Conn.halt() - end - end - - def call(_api, %{method: "OPTIONS"} = conn, _params) do - Shapes.Api.options(conn) - end - - def response(api, _conn, params) do - case Shapes.Api.validate(api, params) do - {:ok, request} -> - { - request, - Shapes.Api.serve_shape_log(request) |> Phoenix.Sync.Electric.consume_response_stream() - } - - {:error, response} -> - {nil, response} - end - end - - def send_response(%ApiAdapter{}, conn, {request, response}) do - conn - |> content_type() - |> Plug.Conn.assign(:request, request) - |> Plug.Conn.assign(:response, response) - |> Shapes.Api.Response.send(response) - end - - defp content_type(conn) do - Plug.Conn.put_resp_content_type(conn, "application/json") - end - end -end diff --git a/lib/phoenix/sync/electric/api_adapter.ex b/lib/phoenix/sync/electric/api_adapter.ex index 84671fd..dc5f528 100644 --- a/lib/phoenix/sync/electric/api_adapter.ex +++ b/lib/phoenix/sync/electric/api_adapter.ex @@ -90,4 +90,76 @@ if Code.ensure_loaded?(Electric.Shapes.Api) do end end end + + defimpl Phoenix.Sync.Adapter.PlugApi, for: Electric.Shapes.Api do + alias Electric.Shapes + + alias Phoenix.Sync.PredefinedShape + alias Phoenix.Sync.Electric.ApiAdapter + + def predefined_shape(api, %PredefinedShape{} = shape) do + ApiAdapter.new(api, shape) + end + + def call(api, %{method: "GET"} = conn, params) do + case Shapes.Api.validate(api, params) do + {:ok, request} -> + conn + |> content_type() + |> Plug.Conn.assign(:request, request) + |> Shapes.Api.serve_shape_log(request) + + {:error, response} -> + conn + |> content_type() + |> Shapes.Api.Response.send(response) + |> Plug.Conn.halt() + end + end + + def call(api, %{method: "DELETE"} = conn, params) do + case Shapes.Api.validate_for_delete(api, params) do + {:ok, request} -> + conn + |> content_type() + |> Plug.Conn.assign(:request, request) + |> Shapes.Api.delete_shape(request) + + {:error, response} -> + conn + |> content_type() + |> Shapes.Api.Response.send(response) + |> Plug.Conn.halt() + end + end + + def call(_api, %{method: "OPTIONS"} = conn, _params) do + Shapes.Api.options(conn) + end + + def response(api, _conn, params) do + case Shapes.Api.validate(api, params) do + {:ok, request} -> + { + request, + Shapes.Api.serve_shape_log(request) |> Phoenix.Sync.Electric.consume_response_stream() + } + + {:error, response} -> + {nil, response} + end + end + + def send_response(_api, conn, {request, response}) do + conn + |> content_type() + |> Plug.Conn.assign(:request, request) + |> Plug.Conn.assign(:response, response) + |> Shapes.Api.Response.send(response) + end + + defp content_type(conn) do + Plug.Conn.put_resp_content_type(conn, "application/json") + end + end end diff --git a/lib/phoenix/sync/sandbox.ex b/lib/phoenix/sync/sandbox.ex index d80c1e3..4367ea2 100644 --- a/lib/phoenix/sync/sandbox.ex +++ b/lib/phoenix/sync/sandbox.ex @@ -259,7 +259,7 @@ if Phoenix.Sync.sandbox_enabled?() do # mark the stack as ready Electric.StatusMonitor.mark_pg_lock_acquired(stack_id, owner) Electric.StatusMonitor.mark_replication_client_ready(stack_id, owner) - Electric.StatusMonitor.mark_connection_pool_ready(stack_id, owner) + mark_connection_pool_ready(stack_id, owner) api_config = Sandbox.Stack.config(stack_id, repo) api = Electric.Application.api(api_config) @@ -476,5 +476,14 @@ if Phoenix.Sync.sandbox_enabled?() do end end end + + # Handle both Electric API versions - older uses /2, newer uses /3 + defp mark_connection_pool_ready(stack_id, owner) do + if function_exported?(Electric.StatusMonitor, :mark_connection_pool_ready, 3) do + apply(Electric.StatusMonitor, :mark_connection_pool_ready, [stack_id, owner, 1]) + else + apply(Electric.StatusMonitor, :mark_connection_pool_ready, [stack_id, owner]) + end + end end end diff --git a/lib/phoenix/sync/sandbox/inspector.ex b/lib/phoenix/sync/sandbox/inspector.ex index 9e33a66..863ba0d 100644 --- a/lib/phoenix/sync/sandbox/inspector.ex +++ b/lib/phoenix/sync/sandbox/inspector.ex @@ -34,6 +34,9 @@ if Phoenix.Sync.sandbox_enabled?() do @impl Electric.Postgres.Inspector def list_relations_with_stale_cache(_), do: {:ok, []} + @impl Electric.Postgres.Inspector + def load_supported_features(_opts), do: {:ok, []} + def start_link(args) do GenServer.start_link(__MODULE__, args, name: name(args[:stack_id])) end diff --git a/lib/phoenix/sync/sandbox/publication_manager.ex b/lib/phoenix/sync/sandbox/publication_manager.ex index 97ed390..1d5eb94 100644 --- a/lib/phoenix/sync/sandbox/publication_manager.ex +++ b/lib/phoenix/sync/sandbox/publication_manager.ex @@ -56,5 +56,9 @@ if Phoenix.Sync.sandbox_enabled?() do def refresh_publication(_opts) do :ok end + + def wait_for_restore(_opts) do + :ok + end end end diff --git a/lib/phoenix/sync/sandbox/stack.ex b/lib/phoenix/sync/sandbox/stack.ex index e5be0f6..537678b 100644 --- a/lib/phoenix/sync/sandbox/stack.ex +++ b/lib/phoenix/sync/sandbox/stack.ex @@ -86,16 +86,17 @@ if Phoenix.Sync.sandbox_enabled?() do %{stack_id: stack_id, table_base_name: :"#{stack_id}"} } + shape_status_config = + struct!(Electric.ShapeCache.ShapeStatus, + shape_meta_table: Electric.ShapeCache.ShapeStatus.shape_meta_table(stack_id), + storage: storage + ) + [ purge_all_shapes?: false, stack_id: stack_id, storage: storage, - shape_status: - {Electric.ShapeCache.ShapeStatus, - Electric.ShapeCache.ShapeStatus.opts( - shape_meta_table: Electric.ShapeCache.ShapeStatus.shape_meta_table(stack_id), - storage: storage - )}, + shape_status: {Electric.ShapeCache.ShapeStatus, shape_status_config}, inspector: inspector, publication_manager: publication_manager_spec, chunk_bytes_threshold: 10_485_760, diff --git a/mix.exs b/mix.exs index 8060362..6eefa3f 100644 --- a/mix.exs +++ b/mix.exs @@ -2,8 +2,8 @@ defmodule Phoenix.Sync.MixProject do use Mix.Project # Remember to update the README when you change the version - @version "0.6.1" - @electric_version ">= 1.1.9 and <= 1.1.10" + @version "0.6.2" + @electric_version ">= 1.1.9 and <= 1.2.4" def project do [