diff --git a/config/dev.exs b/config/dev.exs
index 27025c0a7..e4eaf5c5f 100644
--- a/config/dev.exs
+++ b/config/dev.exs
@@ -60,6 +60,9 @@ config :sanbase, Sanbase.KafkaExporter, producer: Sanbase.InMemoryKafka.Producer
# with. When running the app locally these values are overridden by the values
# in the .env.dev or dev.secret.exs files, which are ignored by git and not
# published in the repository. Please do not report these as security issues.
+# To create the user for your local env:
+# In psql: CREATE ROLE postgres WITH LOGIN SUPERUSER PASSWORD 'postgres';
+# In the terminal: mix ecto.setup
config :sanbase, Sanbase.Repo,
username: "postgres",
password: "postgres",
diff --git a/lib/sanbase/metric/registry/populate.ex b/lib/sanbase/metric/registry/populate.ex
index e37494cfd..97c4564b7 100644
--- a/lib/sanbase/metric/registry/populate.ex
+++ b/lib/sanbase/metric/registry/populate.ex
@@ -161,17 +161,8 @@ defmodule Sanbase.Metric.Registry.Populate do
if inserts > 0 or updates > 0 do
map = %{inserts_count: inserts, updates_count: updates}
- {inserted_metrics, updated_metrics} =
- Enum.reduce(list, {[], []}, fn {type, record}, {insert_acc, update_acc} ->
- case type do
- :insert -> {[record.metric | insert_acc], update_acc}
- :update -> {insert_acc, [record.metric | update_acc]}
- _ -> {insert_acc, update_acc}
- end
- end)
-
- # Emit locally event with more data. The distributed events are used only to refresh the
- # persistent term, the local node event will also trigger notifications
+ {inserted_metrics, updated_metrics} = extract_inserted_updated_metrics(list)
+ # Emit locally event with more data
local_event_map =
Map.merge(map, %{inserted_metrics: inserted_metrics, updated_metrics: updated_metrics})
@@ -181,22 +172,80 @@ defmodule Sanbase.Metric.Registry.Populate do
%{}
)
- # Emit distributed event only to the MetricRegistrySubscriber so it refreshed the stored data
- # in persistent term
- Node.list()
- |> Enum.each(fn node ->
- IO.puts("Emitting event :bulk_metric_registry_change to #{node}")
-
- Node.spawn(node, fn ->
- Sanbase.Metric.Registry.EventEmitter.emit_event(
- {:ok, map},
- :bulk_metric_registry_change,
- %{__only_process_by__: [Sanbase.EventBus.MetricRegistrySubscriber]}
- )
- end)
- end)
+ # Emit distributed event
+ emit_distributed_event(map)
else
:ok
end
end
+
+ defp extract_inserted_updated_metrics(list) do
+ {_inserted_metrics, _updated_metrics} =
+ Enum.reduce(list, {[], []}, fn {type, record}, {insert_acc, update_acc} ->
+ case type do
+ :insert -> {[record.metric | insert_acc], update_acc}
+ :update -> {insert_acc, [record.metric | update_acc]}
+ _ -> {insert_acc, update_acc}
+ end
+ end)
+ end
+
+ defp emit_distributed_event(map) do
+ Node.list()
+ |> Enum.each(fn node ->
+ IO.puts("Emitting event :bulk_metric_registry_change to #{node}")
+
+ Node.spawn(node, fn ->
+ Sanbase.Metric.Registry.EventEmitter.emit_event(
+ {:ok, map},
+ :bulk_metric_registry_change,
+ %{__only_process_by__: [Sanbase.EventBus.MetricRegistrySubscriber]}
+ )
+ end)
+ end)
+ end
+end
+
+defmodule Stack do
+ use GenServer
+
+ def start_link(initial_stack \\ []) do
+ GenServer.start_link(__MODULE__, initial_stack, name: __MODULE__)
+ end
+
+ def push(element) do
+ GenServer.call(__MODULE__, {:push, element})
+ end
+
+ def pop do
+ GenServer.call(__MODULE__, :pop)
+ end
+
+ def peek do
+ GenServer.call(__MODULE__, :peek)
+ end
+
+ def init(initial_stack) do
+ {:ok, initial_stack}
+ end
+
+ def handle_call({:push, element}, _from, stack) do
+ {:reply, :ok, [element | stack]}
+ end
+
+ def handle_call(:pop, _from, [h | t]) do
+ {:reply, h, t}
+ end
+
+ def handle_call(:pop, _from, []) do
+ {:reply, nil, []}
+ end
+
+ def handle_call(:peek, _from, [h | _] = stack) do
+ {:reply, h, stack}
+ end
+
+ def handle_call(:peek, _from, []) do
+ {:reply, nil, []}
+ end
end
diff --git a/lib/sanbase/metric/registry/registry.ex b/lib/sanbase/metric/registry/registry.ex
index b7ec04b8f..28b5b1959 100644
--- a/lib/sanbase/metric/registry/registry.ex
+++ b/lib/sanbase/metric/registry/registry.ex
@@ -263,10 +263,13 @@ defmodule Sanbase.Metric.Registry do
"""
@spec all() :: [t()]
def all() do
- query =
- from(m in __MODULE__,
- order_by: [asc: m.id]
- )
+ query = from(m in __MODULE__, order_by: [asc: m.id])
+
+ Sanbase.Repo.all(query)
+ end
+
+ def by_ids(ids) do
+ query = from(m in __MODULE__, where: m.id in ^ids)
Sanbase.Repo.all(query)
end
diff --git a/lib/sanbase/metric/registry/sync.ex b/lib/sanbase/metric/registry/sync.ex
new file mode 100644
index 000000000..2d2e0ecd7
--- /dev/null
+++ b/lib/sanbase/metric/registry/sync.ex
@@ -0,0 +1,119 @@
+defmodule Sanbase.Metric.Registry.Sync do
+ @moduledoc """
+ This module is responsible for syncing the metric registry with the external
+ source of truth.
+ """
+
+ alias Sanbase.Metric.Registry
+ @process_name :metric_registry_sync
+
+ @spec initiate(list(non_neg_integer())) :: :ok
+ def initiate(metric_registry_ids) when is_list(metric_registry_ids) do
+ case ongoing_sync?() do
+ true ->
+ {:error, "Sync process is already running"}
+
+ false ->
+ pid = spawn_link(__MODULE__, :sync, [metric_registry_ids])
+ Process.register(pid, @process_name)
+ {:ok, pid}
+ end
+ end
+
+ def initiate_sync(metric_registry_ids) do
+ with :ok <- check_initiate_env(),
+ {:ok, content} <- get_sync_content(metric_registry_ids),
+ :ok <- start_sync(content),
+ :ok <- record_sync(content) do
+ {:ok, content}
+ end
+ end
+
+ def apply_sync(json_content) do
+ with :ok <- check_apply_env(),
+ {:ok, list} when is_list(list) <- Jason.decode(json_content) do
+ for %{} = params <- list do
+ params = Map.put(params, "sync_status", "synced")
+
+ %{"metric" => metric, "data_type" => data_type, "fixed_parameters" => fixed_parameters} =
+ params
+
+ with {:ok, metric} <- Registry.by_name(metric, data_type, fixed_parameters),
+ {:ok, metric} <- Registry.update(metric, params) do
+ {:ok, metric}
+ end
+ |> dbg()
+ end
+ end
+ end
+
+ defp start_sync(content) do
+ url = get_sync_target_url()
+
+ case Req.post(url, json: content) do
+ {:ok, _} -> :ok
+ {:error, _} -> {:error, "Failed to sync"}
+ end
+ end
+
+ defp get_sync_target_url() do
+ secret = System.get_env("METRIC_REGISTRY_SYNC_SECRET")
+
+ case Sanbase.Utils.Config.module_get(Sanbase, :deployment_env) do
+ "dev" -> "http://localhost:4000/sync_metric_registry/#{secret}"
+ "stage" -> "http://api.santiment.net/sync_metric_registry/#{secret}"
+ "prod" -> raise("Cannot initiate sync from PROD")
+ end
+ end
+
+ defp get_sync_content([]), do: {:error, "Nothing to sync"}
+
+ defp get_sync_content(metric_registry_ids) do
+ structs = Sanbase.Metric.Registry.by_ids(metric_registry_ids)
+ content = generate_content(structs)
+ {:ok, content}
+ end
+
+ defp check_initiate_env() do
+ deployment_env = Sanbase.Utils.Config.module_get(Sanbase, :deployment_env)
+ database_url = System.get_env("DATABASE_URL")
+
+ # If local, the DATABASE_URL should not be set pointing to stage/prod.
+ # Only work if the local postgres is used
+ local? = deployment_env == "env" and is_nil(database_url)
+ stage? = deployment_env == "stage"
+
+ if local? or stage? do
+ :ok
+ else
+ {:error, "Can only deploy sync from STAGE to PROD"}
+ end
+ end
+
+ defp check_apply_env() do
+ deployment_env = Sanbase.Utils.Config.module_get(Sanbase, :deployment_env)
+ database_url = System.get_env("DATABASE_URL")
+
+ # If local, the DATABASE_URL should not be set pointing to stage/prod.
+ # Only work if the local postgres is used
+ local? = deployment_env == "env" and is_nil(database_url)
+ prod? = deployment_env == "prod"
+
+ if local? or prod? do
+ :ok
+ else
+ {:error, "Can only apply sync only on PROD"}
+ end
+ end
+
+ defp generate_content(structs) do
+ api_url = SanbaseWeb.Endpoint.api_url()
+ from_host = URI.parse(api_url).host
+
+ %{
+ metric_registry_structs: Jason.encode!(structs),
+ generated_at: DateTime.utc_now() |> DateTime.to_iso8601(),
+ from_host: from_host
+ }
+ end
+end
diff --git a/lib/sanbase/metric/registry/sync_server.ex b/lib/sanbase/metric/registry/sync_server.ex
new file mode 100644
index 000000000..b741e5d46
--- /dev/null
+++ b/lib/sanbase/metric/registry/sync_server.ex
@@ -0,0 +1,11 @@
+defmodule Sanbase.Metric.Registry.SyncServer do
+ use GenServer
+ @name :metric_registry_sync_server
+ def start_link(_opts) do
+ GenServer.start_link(__MODULE__, name: @name)
+ end
+
+ def init(_) do
+ {:ok, %{}}
+ end
+end
diff --git a/lib/sanbase_web/controllers/metric_registry_controller.ex b/lib/sanbase_web/controllers/metric_registry_controller.ex
index a9377c269..57b7d2fa8 100644
--- a/lib/sanbase_web/controllers/metric_registry_controller.ex
+++ b/lib/sanbase_web/controllers/metric_registry_controller.ex
@@ -1,13 +1,57 @@
defmodule SanbaseWeb.MetricRegistryController do
use SanbaseWeb, :controller
+ def sync(conn, %{"secret" => secret} = params) do
+ case secret == get_sync_secret() do
+ true ->
+ # TODO: Remove
+ IO.inspect(params)
+
+ conn
+ |> resp(200, "OK")
+ |> send_resp()
+
+ false ->
+ conn
+ |> resp(403, "Unauthorized")
+ |> send_resp()
+ end
+ end
+
def export_json(conn, _params) do
conn
- |> resp(200, "ok")
+ |> resp(200, get_metric_registry_json())
|> send_resp()
end
defp get_metric_registry_json() do
Sanbase.Metric.Registry.all()
+ |> Enum.take(1)
+ |> Enum.map(&transform/1)
+
+ # |> Enum.map(&Jason.encode!/1)
+ # |> Enum.intersperse("\n")
+ end
+
+ defp transform(struct) when is_struct(struct) do
+ struct
+ |> Map.from_struct()
+ |> Map.drop([:__meta__, :inserted_at, :updated_at, :change_suggestions])
+ |> Map.new(fn
+ {k, v} when is_list(v) ->
+ {k, Enum.map(v, &transform/1)}
+
+ {k, v} when is_map(v) ->
+ {k, transform(v)}
+
+ {k, v} ->
+ {k, v}
+ end)
+ end
+
+ defp transform(data), do: data
+
+ defp get_sync_secret() do
+ Sanbase.Utils.Config.module_get(__MODULE__, :sync_secret, "no_secret")
end
end
diff --git a/lib/sanbase_web/live/metric_registry/metric_registry_index_live.ex b/lib/sanbase_web/live/metric_registry/metric_registry_index_live.ex
index c9c907918..c75ba9b7e 100644
--- a/lib/sanbase_web/live/metric_registry/metric_registry_index_live.ex
+++ b/lib/sanbase_web/live/metric_registry/metric_registry_index_live.ex
@@ -38,7 +38,7 @@ defmodule SanbaseWeb.MetricRegistryIndexLive do
- Showing <%= length(@visible_metrics_ids) %> metrics
+ Showing {length(@visible_metrics_ids)} metrics
<.navigation />
@@ -50,7 +50,7 @@ defmodule SanbaseWeb.MetricRegistryIndexLive do
<:col :let={row} label="ID">
{row.id}
- <:col :let={row} label="Metric Names" col_class="max-w-[720px] break-all">
+ <:col :let={row} label="Metric Names" col_class="max-w-[420px]">
<.metric_names
metric={row.metric}
internal_metric={row.internal_metric}
@@ -67,7 +67,6 @@ defmodule SanbaseWeb.MetricRegistryIndexLive do
<:col
:let={row}
-<<<<<<< HEAD
label="Table"
popover_target="popover-table"
popover_target_text={get_popover_text(%{key: "Clickhouse Table"})}
@@ -84,25 +83,6 @@ defmodule SanbaseWeb.MetricRegistryIndexLive do
<:col
:let={row}
-||||||| parent of e83704e2d (Add is_verified toggle to Metric Registry LiveView)
- label="Table"
- popover_target="popover-table"
- popover_target_text={get_popover_text(%{key: "Clickhouse Table"})}
- >
- <.embeded_schema_show list={row.tables} key={:name} />
-
- <:col
- :let={row}
- label="Default Aggregation"
- popover_target="popover-default-aggregation"
- popover_target_text={get_popover_text(%{key: "Default Aggregation"})}
- >
- <%= row.default_aggregation %>
-
- <:col
- :let={row}
-=======
->>>>>>> e83704e2d (Add is_verified toggle to Metric Registry LiveView)
label="Access"
popover_target="popover-access"
popover_target_text={get_popover_text(%{key: "Access"})}
@@ -212,46 +192,48 @@ defmodule SanbaseWeb.MetricRegistryIndexLive do
)}
end
- defp list_metrics_verified_status_changed(assigns) do
+ defp embeded_schema_show(assigns) do
~H"""
-<<<<<<< HEAD
{Map.get(item, @key)}
-||||||| parent of e83704e2d (Add is_verified toggle to Metric Registry LiveView)
-
-
- <%= Map.get(item, @key) %>
-=======
-
- No changes
+
-
- <.table id="uploaded_images" rows={Enum.filter(@metrics, &(&1.id in @changed_metrics_ids))}>
- <:col :let={row} label="Metric">
- <.metric_names
- metric={row.metric}
- internal_metric={row.internal_metric}
- human_readable_name={row.human_readable_name}
+ """
+ end
+
+ defp list_metrics_verified_status_changed(assigns) do
+ ~H"""
+
+
+ No changes
+
+
+ <.table id="uploaded_images" rows={Enum.filter(@metrics, &(&1.id in @changed_metrics_ids))}>
+ <:col :let={row} label="Metric" col_class="max-w-[420px]">
+ <.metric_names
+ metric={row.metric}
+ internal_metric={row.internal_metric}
+ human_readable_name={row.human_readable_name}
+ />
+
+ <:col :let={row} label="New Status">
+
VERIFIED
+
UNVERIFIED
+
+
+
+ <.phx_click_button
+ phx_click="confirm_verified_changes_update"
+ class="bg-green-500 hover:bg-green-900 text-white"
+ text="Confirm Changes"
/>
-
- <:col :let={row} label="New Status">
-
VERIFIED
-
UNVERIFIED
-
-
-
- <.phx_click_button
- phx_click="confirm_verified_changes_update"
- class="bg-green-500 hover:bg-green-900 text-white"
- text="Confirm Changes"
- />
- <.phx_click_button
- phx_click="hide_show_verified_changes_modal"
- class="bg-white hover:bg-gray-100 text-gray-800"
- text="Close"
- />
->>>>>>> e83704e2d (Add is_verified toggle to Metric Registry LiveView)
+ <.phx_click_button
+ phx_click="hide_show_verified_changes_modal"
+ class="bg-white hover:bg-gray-100 text-gray-800"
+ text="Close"
+ />
+
"""
@@ -398,15 +380,15 @@ defmodule SanbaseWeb.MetricRegistryIndexLive do
@class
]}
>
- <%= @text %>
-
(<%= @count %>)
+ {@text}
+
({@count})
"""
end
defp metric_names(assigns) do
~H"""
-
+
{@human_readable_name}
{@metric} (API)
{@internal_metric} (DB)
diff --git a/lib/sanbase_web/live/metric_registry/metric_registry_sync_live.ex b/lib/sanbase_web/live/metric_registry/metric_registry_sync_live.ex
index 007fa5c96..e8f2345d7 100644
--- a/lib/sanbase_web/live/metric_registry/metric_registry_sync_live.ex
+++ b/lib/sanbase_web/live/metric_registry/metric_registry_sync_live.ex
@@ -4,13 +4,23 @@ defmodule SanbaseWeb.MetricRegistrySyncLive do
alias SanbaseWeb.AvailableMetricsComponents
@impl true
def mount(_params, _session, socket) do
- metrics = Sanbase.Metric.Registry.all() |> Enum.filter(&(&1.sync_status == "not_synced"))
+ metrics = Sanbase.Metric.Registry.all()
+
+ syncable_metrics =
+ metrics
+ |> Enum.filter(&(&1.sync_status == "not_synced" and &1.is_verified == true))
+
+ not_syncable_metrics =
+ metrics
+ |> Enum.filter(&(&1.sync_status == "not_synced" and &1.is_verified == false))
{:ok,
socket
|> assign(
- metrics: metrics,
- sync_metric_ids: Enum.map(metrics, & &1.id) |> MapSet.new()
+ syncable_metrics: syncable_metrics,
+ non_syncable_metrics: not_syncable_metrics,
+ metric_ids_to_sync: Enum.map(syncable_metrics, & &1.id) |> MapSet.new(),
+ page_title: "Syncing Metrics"
)}
end
@@ -20,7 +30,10 @@ defmodule SanbaseWeb.MetricRegistrySyncLive do
- Showing <%= length(@metrics) %> metrics that are not synced
+ {length(@syncable_metrics)} metric(s) available to be synced from stage to prod}
+
+
+ {length(@non_syncable_metrics)} metric(s) not synced but need(s) to be verified first}
@@ -42,12 +55,12 @@ defmodule SanbaseWeb.MetricRegistrySyncLive do
class="bg-white hover:bg-gray-100 text-zync-900"
/>
- <.table id="metrics_registry" rows={@metrics}>
+ <.table id="metrics_registry" rows={@syncable_metrics}>
<:col :let={row} label="Should Sync">
- <.checkbox row={row} sync_metric_ids={@sync_metric_ids} />
+ <.checkbox row={row} metric_ids_to_sync={@metric_ids_to_sync} />
<:col :let={row} label="ID">
- <%= row.id %>
+ {row.id}
<:col :let={row} label="Metric Names" col_class="max-w-[720px] break-all">
<.metric_names
@@ -61,7 +74,7 @@ defmodule SanbaseWeb.MetricRegistrySyncLive do
text="Sync Metrics"
phx_click="sync"
class="bg-blue-700 hover:bg-blue-800 text-white"
- count={MapSet.size(@sync_metric_ids)}
+ count={MapSet.size(@metric_ids_to_sync)}
phx_disable_with="Syncing..."
/>
@@ -85,46 +98,52 @@ defmodule SanbaseWeb.MetricRegistrySyncLive do
]}
phx-disable-with={@phx_disable_with}
>
- <%= @text %>
-
(<%= @count %>)
+ {@text}
+
({@count})
"""
end
@impl true
def handle_event("sync", _params, socket) do
- Process.sleep(5000)
+ ids = socket.assigns.metric_ids_to_sync |> Enum.to_list()
+
+ case Sanbase.Metric.Registry.Sync.initiate_sync(ids) do
+ {:ok, data} -> {:ok, data}
+ {:error, error} -> {:error, error}
+ end
+
{:noreply, socket}
end
def handle_event("update_should_sync", %{"metric_registry_id" => id} = params, socket) do
checked = Map.get(params, "value") == "on"
- sync_metric_ids =
+ metric_ids_to_sync =
if checked do
- MapSet.put(socket.assigns.sync_metric_ids, id)
+ MapSet.put(socket.assigns.metric_ids_to_sync, id)
else
- MapSet.delete(socket.assigns.sync_metric_ids, id)
+ MapSet.delete(socket.assigns.metric_ids_to_sync, id)
end
- {:noreply, assign(socket, sync_metric_ids: sync_metric_ids)}
+ {:noreply, assign(socket, metric_ids_to_sync: metric_ids_to_sync)}
end
def handle_event("select_all", _params, socket) do
{:noreply,
- assign(socket, sync_metric_ids: Enum.map(socket.assigns.metrics, & &1.id) |> MapSet.new())}
+ assign(socket, metric_ids_to_sync: Enum.map(socket.assigns.metrics, & &1.id) |> MapSet.new())}
end
def handle_event("deselect_all", _params, socket) do
- {:noreply, assign(socket, sync_metric_ids: MapSet.new())}
+ {:noreply, assign(socket, metric_ids_to_sync: MapSet.new())}
end
defp metric_names(assigns) do
~H"""
-
<%= @human_readable_name %>
-
<%= @metric %> (API)
-
<%= @internal_metric %> (DB)
+
{@human_readable_name}
+
{@metric} (API)
+
{@internal_metric} (DB)
"""
end
@@ -136,7 +155,7 @@ defmodule SanbaseWeb.MetricRegistrySyncLive do
id="not-verified-only"
name={"sync-status-#{@row.id}"}
type="checkbox"
- checked={@row.id in @sync_metric_ids}
+ checked={@row.id in @metric_ids_to_sync}
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded"
phx-click={JS.push("update_should_sync", value: %{metric_registry_id: @row.id})}
}
diff --git a/lib/sanbase_web/router.ex b/lib/sanbase_web/router.ex
index 40d8b77d3..c85ecb2ba 100644
--- a/lib/sanbase_web/router.ex
+++ b/lib/sanbase_web/router.ex
@@ -90,8 +90,6 @@ defmodule SanbaseWeb.Router do
live("/metric_registry/sync", MetricRegistrySyncLive, :new)
end
- get("/metric_registry_export", MetricRegistryController, :export_json)
-
scope "/" do
pipe_through(:api)
@@ -183,6 +181,9 @@ defmodule SanbaseWeb.Router do
end
scope "/", SanbaseWeb do
+ get("/metric_registry_export", MetricRegistryController, :export_json)
+ post("/sync_metric_registry/:secret", MetricRegistryController, :sync)
+
get("/api_metric_name_mapping", MetricNameController, :api_metric_name_mapping)
get("/projects_data", DataController, :projects_data)
get("/projects_twitter_handles", DataController, :projects_twitter_handles)
diff --git a/priv/repo/migrations/20241218134556_add_metric_registry_syncs.exs b/priv/repo/migrations/20241218134556_add_metric_registry_syncs.exs
new file mode 100644
index 000000000..3e181ba74
--- /dev/null
+++ b/priv/repo/migrations/20241218134556_add_metric_registry_syncs.exs
@@ -0,0 +1,11 @@
+defmodule Sanbase.Repo.Migrations.AddMetricRegistrySyncs do
+ use Ecto.Migration
+
+ def change do
+ create table(:metric_registry_syncs) do
+ add(:state, :string)
+ add(:content, :map)
+ timestamps()
+ end
+ end
+end
diff --git a/priv/repo/structure.sql b/priv/repo/structure.sql
index 83fc57aaa..0c74a57eb 100644
--- a/priv/repo/structure.sql
+++ b/priv/repo/structure.sql
@@ -2,8 +2,8 @@
-- PostgreSQL database dump
--
--- Dumped from database version 15.1 (Homebrew)
--- Dumped by pg_dump version 15.1 (Homebrew)
+-- Dumped from database version 15.10 (Homebrew)
+-- Dumped by pg_dump version 15.10 (Homebrew)
SET statement_timeout = 0;
SET lock_timeout = 0;
@@ -2368,6 +2368,38 @@ CREATE SEQUENCE public.metric_registry_id_seq
ALTER SEQUENCE public.metric_registry_id_seq OWNED BY public.metric_registry.id;
+--
+-- Name: metric_registry_syncs; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE public.metric_registry_syncs (
+ id bigint NOT NULL,
+ state character varying(255),
+ content jsonb,
+ inserted_at timestamp without time zone NOT NULL,
+ updated_at timestamp without time zone NOT NULL
+);
+
+
+--
+-- Name: metric_registry_syncs_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE public.metric_registry_syncs_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: metric_registry_syncs_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE public.metric_registry_syncs_id_seq OWNED BY public.metric_registry_syncs.id;
+
+
--
-- Name: metrics; Type: TABLE; Schema: public; Owner: -
--
@@ -5264,6 +5296,13 @@ ALTER TABLE ONLY public.metric_registry ALTER COLUMN id SET DEFAULT nextval('pub
ALTER TABLE ONLY public.metric_registry_change_suggestions ALTER COLUMN id SET DEFAULT nextval('public.metric_registry_change_suggestions_id_seq'::regclass);
+--
+-- Name: metric_registry_syncs id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.metric_registry_syncs ALTER COLUMN id SET DEFAULT nextval('public.metric_registry_syncs_id_seq'::regclass);
+
+
--
-- Name: metrics id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -6177,6 +6216,14 @@ ALTER TABLE ONLY public.metric_registry
ADD CONSTRAINT metric_registry_pkey PRIMARY KEY (id);
+--
+-- Name: metric_registry_syncs metric_registry_syncs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.metric_registry_syncs
+ ADD CONSTRAINT metric_registry_syncs_pkey PRIMARY KEY (id);
+
+
--
-- Name: metrics metrics_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -9724,14 +9771,15 @@ INSERT INTO public."schema_migrations" (version) VALUES (20241029080754);
INSERT INTO public."schema_migrations" (version) VALUES (20241029082533);
INSERT INTO public."schema_migrations" (version) VALUES (20241029151959);
INSERT INTO public."schema_migrations" (version) VALUES (20241030141825);
-INSERT INTO public."schema_migrations" (version) VALUES (20241104061632);
INSERT INTO public."schema_migrations" (version) VALUES (20241104115340);
INSERT INTO public."schema_migrations" (version) VALUES (20241108112754);
INSERT INTO public."schema_migrations" (version) VALUES (20241112094924);
INSERT INTO public."schema_migrations" (version) VALUES (20241114140339);
INSERT INTO public."schema_migrations" (version) VALUES (20241114141110);
INSERT INTO public."schema_migrations" (version) VALUES (20241116104556);
+INSERT INTO public."schema_migrations" (version) VALUES (20241121133719);
INSERT INTO public."schema_migrations" (version) VALUES (20241128113958);
INSERT INTO public."schema_migrations" (version) VALUES (20241128161315);
INSERT INTO public."schema_migrations" (version) VALUES (20241202104812);
INSERT INTO public."schema_migrations" (version) VALUES (20241212054904);
+INSERT INTO public."schema_migrations" (version) VALUES (20241218134556);
diff --git a/test/sanbase/metric_registry/metric_registry_sync_test.exs b/test/sanbase/metric_registry/metric_registry_sync_test.exs
new file mode 100644
index 000000000..943e74d5c
--- /dev/null
+++ b/test/sanbase/metric_registry/metric_registry_sync_test.exs
@@ -0,0 +1,12 @@
+defmodule Sanbase.MetricRegistrySyncTest do
+ use SanbaseWeb.ConnCase
+
+ test "syncing", context do
+ context.conn
+ |> post("/sync_metric_registry/no_secret", %{
+ metrics: [%Sanbase.Metric.Registry{}, "a1", "b2", "c3"]
+ })
+
+ assert 1 == 2
+ end
+end