From de50a067c75018e9a7462b49e3115a212c142ff5 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Thu, 7 Nov 2024 16:50:16 +0200 Subject: [PATCH] Rework Metric.Registry table. Improve edit/new form --- lib/sanbase/metric/registry/event_emitter.ex | 4 + lib/sanbase/metric/registry/populate.ex | 3 +- lib/sanbase/metric/registry/registry.ex | 26 +- lib/sanbase/metric/registry/validation.ex | 2 +- .../metric_registry_form_live.ex | 302 ++++++++++++------ .../metric_registry_show_live.ex | 12 +- .../20241104115340_create_metric_registry.exs | 5 +- priv/repo/structure.sql | 4 +- 8 files changed, 234 insertions(+), 124 deletions(-) diff --git a/lib/sanbase/metric/registry/event_emitter.ex b/lib/sanbase/metric/registry/event_emitter.ex index a53da5991..7d2bd25c0 100644 --- a/lib/sanbase/metric/registry/event_emitter.ex +++ b/lib/sanbase/metric/registry/event_emitter.ex @@ -21,6 +21,10 @@ defmodule Sanbase.Metric.Registry.EventEmitter do |> notify() end + def handle_event({:error, _changeset}, _event_type, _args) do + :ok + end + defp notify(data) do Sanbase.EventBus.notify(%{topic: @topic, data: data}) :ok diff --git a/lib/sanbase/metric/registry/populate.ex b/lib/sanbase/metric/registry/populate.ex index 4cdefaab5..1bce5e5f2 100644 --- a/lib/sanbase/metric/registry/populate.ex +++ b/lib/sanbase/metric/registry/populate.ex @@ -35,7 +35,8 @@ defmodule Sanbase.Metric.Registry.Populate do is_timebound: Map.get(map, "is_timebound", false), metric: map["name"], min_interval: map["min_interval"], - min_plan: Map.get(map, "min_plan", %{}), + sanbase_min_plan: get_in(map, ["min_plan", "SANBASE"]) || "free", + sanapi_min_plan: get_in(map, ["min_plan", "SANAPI"]) || "free", parameters: Map.get(map, "parameters", []), required_selectors: Map.get(map, "required_selectors", []) |> Enum.map(&%{type: &1}), selectors: Map.get(map, "selectors", []) |> Enum.map(&%{type: &1}), diff --git a/lib/sanbase/metric/registry/registry.ex b/lib/sanbase/metric/registry/registry.ex index d2099dce3..8211f1a7c 100644 --- a/lib/sanbase/metric/registry/registry.ex +++ b/lib/sanbase/metric/registry/registry.ex @@ -13,6 +13,8 @@ defmodule Sanbase.Metric.Registry do @human_readable_name_regex ~r|^[a-zA-Z0-9_\.\-{}():/\\ ]+$| @aggregations ["sum", "last", "count", "avg", "max", "min", "first"] def aggregations(), do: @aggregations + @metric_regex ~r/^[a-z0-9_{}:]+$/ + def metric_regex(), do: @metric_regex @type t :: %__MODULE__{ id: integer(), @@ -24,7 +26,8 @@ defmodule Sanbase.Metric.Registry do default_aggregation: String.t(), min_interval: String.t(), access: String.t(), - min_plan: map(), + sanbase_min_plan: String.t(), + sanapi_min_plan: String.t(), selectors: [String.t()], required_selectors: [String.t()], is_template: boolean(), @@ -32,7 +35,6 @@ defmodule Sanbase.Metric.Registry do fixed_parameters: map(), is_timebound: boolean(), has_incomplete_data: boolean(), - is_exposed: boolean(), exposed_environments: String.t(), is_hidden: boolean(), is_deprecated: boolean(), @@ -71,6 +73,7 @@ defmodule Sanbase.Metric.Registry do struct |> cast(attrs, [:name]) |> validate_required([:name]) + |> validate_format(:name, ~r/[a-z0-9_\-]/) end end @@ -85,7 +88,9 @@ defmodule Sanbase.Metric.Registry do def changeset(%__MODULE__{} = struct, attrs) do struct |> cast(attrs, [:name]) - |> validate_required([:name]) + |> validate_required(:name) + |> validate_format(:name, Sanbase.Metric.Registry.metric_regex()) + |> validate_length(:name, min: 3, max: 100) end end @@ -118,7 +123,8 @@ defmodule Sanbase.Metric.Registry do field(:default_aggregation, :string) field(:min_interval, :string) field(:access, :string) - field(:min_plan, :map) + field(:sanbase_min_plan, :string) + field(:sanapi_min_plan, :string) embeds_many(:selectors, Selector, on_replace: :delete) embeds_many(:required_selectors, Selector, on_replace: :delete) @@ -131,7 +137,6 @@ defmodule Sanbase.Metric.Registry do field(:is_timebound, :boolean, default: false) field(:has_incomplete_data, :boolean, default: false) - field(:is_exposed, :boolean, default: true) field(:exposed_environments, :string, default: "all") field(:is_hidden, :boolean, default: false) @@ -159,13 +164,13 @@ defmodule Sanbase.Metric.Registry do :human_readable_name, :internal_metric, :is_deprecated, - :is_exposed, :is_hidden, :is_template, :is_timebound, :metric, :min_interval, - :min_plan, + :sanbase_min_plan, + :sanapi_min_plan, :parameters ]) |> cast_embed(:selectors, @@ -201,8 +206,8 @@ defmodule Sanbase.Metric.Registry do :metric, :min_interval ]) - |> validate_format(:metric, ~r/^[a-z0-9_{}:]+$/) - |> validate_format(:internal_metric, ~r/^[a-z0-9_{}:]+$/) + |> validate_format(:metric, @metric_regex) + |> validate_format(:internal_metric, @metric_regex) |> validate_format(:human_readable_name, @human_readable_name_regex) |> validate_length(:metric, min: 3, max: 100) |> validate_length(:internal_metric, min: 3, max: 100) @@ -212,7 +217,8 @@ defmodule Sanbase.Metric.Registry do |> validate_inclusion(:exposed_environments, ["all", "none", "stage", "prod"]) |> validate_inclusion(:access, ["free", "restricted"]) |> validate_change(:min_interval, &Validation.validate_min_interval/2) - |> validate_change(:min_plan, &Validation.validate_min_plan/2) + |> validate_inclusion(:sanbase_min_plan, ["free", "pro", "max"]) + |> validate_inclusion(:sanapi_min_plan, ["free", "pro", "max"]) |> Validation.validate_template_fields() |> unique_constraint([:metric, :data_type, :fixed_parameters], name: :metric_registry_composite_unique_index diff --git a/lib/sanbase/metric/registry/validation.ex b/lib/sanbase/metric/registry/validation.ex index 1b86869b8..d468de150 100644 --- a/lib/sanbase/metric/registry/validation.ex +++ b/lib/sanbase/metric/registry/validation.ex @@ -88,7 +88,7 @@ defmodule Sanbase.Metric.Registry.Validation do :parameters, """ The provided parameters do not match the captures in the metric #{metric}. - Captures: #{Enum.join(captures, ", ")} + Captures: #{Enum.join(captures, ", ")}, Parameters: #{Enum.join(parameter_keys, ", ")} """ ) diff --git a/lib/sanbase_web/live/metric_registry/metric_registry_form_live.ex b/lib/sanbase_web/live/metric_registry/metric_registry_form_live.ex index 615fc01ca..e86c2e59b 100644 --- a/lib/sanbase_web/live/metric_registry/metric_registry_form_live.ex +++ b/lib/sanbase_web/live/metric_registry/metric_registry_form_live.ex @@ -21,7 +21,8 @@ defmodule SanbaseWeb.MetricRegistryFormLive do |> assign( metric_registry: metric_registry, age_title: page_title(socket.assigns.live_action), - form: form + form: form, + save_errors: [] )} end @@ -66,30 +67,37 @@ defmodule SanbaseWeb.MetricRegistryFormLive do label="Human Readable Name" /> <.aliases_input form={@form} /> - <.tables_input form={@form} /> + <.min_plan_input form={@form} /> + <.input + type="select" + id="input-access" + field={@form[:access]} + label="Access" + options={["free", "restricted"]} + /> <.input type="text" id="input-min-interval" field={@form[:min_interval]} label="Min Interval" /> <.input type="select" - id="input-aggregation" - field={@form[:aggregation]} + id="input-default-aggregation" + field={@form[:default_aggregation]} label="Default Aggregation" options={Registry.aggregations()} /> <.input type="select" - id="input-access" - field={@form[:access]} - label="Access" - options={["free", "restricted"]} + id="input-has-incomplete-data" + field={@form[:has_incomplete_data]} + label="Has Incomplete Data" + options={[true, false]} /> <.input type="select" id="input-exposed-environments" field={@form[:exposed_environments]} label="Exposed on Environments" - options={["all", "stage", "prod"]} + options={["all", "none", "stage", "prod"]} /> <.input @@ -99,117 +107,183 @@ defmodule SanbaseWeb.MetricRegistryFormLive do label="Access" options={["timeseries", "histogram", "table"]} /> - - <.input type="textarea" id="input-parameters" field={@form[:parameters]} label="Parameters" /> <.selectors_input form={@form} /> <.required_selectors_input form={@form} /> + + <.input + type="select" + id="input-is-hidden" + field={@form[:is_hidden]} + label="Is Hidden" + options={[true, false]} + /> + <.input + type="select" + id="input-is-timebound" + field={@form[:is_timebound]} + label="Is Timebound" + options={[true, false]} + /> + <.input + type="textarea" + id="input-parameters" + field={@form[:parameters]} + value={Jason.encode!(@metric_registry.parameters)} + label="Parameters" + /> <.button phx-disable-with="Saving...">Save + + <.error :for={{field, [msg]} <- @save_errors}><%= to_string(field) <> ": " <> msg %> """ end + def min_plan_input(assigns) do + ~H""" +
+ Min Plan per product + <.input + type="select" + id="input-sanbase-min-plan" + field={@form[:sanbase_min_plan]} + label="Sanbase" + options={["free", "pro", "max"]} + /> + + <.input + type="select" + id="input-sanapi-min-plan" + field={@form[:sanapi_min_plan]} + label="Sanapi" + options={["free", "pro", "max"]} + /> +
+ """ + end + attr :name, :string, required: true attr :text, :string, required: true - attr :icon, :string, required: false, default: nil - attr :fp, :map, required: false, default: nil + attr :ef, :map, required: false, default: nil - def inputs_for_button(assigns) do + def inputs_for_drop_button(assigns) do ~H""" - + attr :name, :string, required: true + attr :text, :string, required: true + + def inputs_for_add_button(assigns) do + ~H""" +
+ """ end - def inputs_for_drop_button(assigns) do + attr :form, :map, required: true + attr :singular, :string, required: true + attr :plural, :string, required: true + attr :form_field, :atom, required: true + attr :embeded_schema_field, :atom, required: true + attr :sort_param, :atom, required: true + attr :drop_param, :atom, required: true + + def embeds_input(assigns) do ~H""" -