From b6bc3c0fd396d36eb07981f83209b207b8148add Mon Sep 17 00:00:00 2001 From: Rekkice <77703180+Rekkice@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:12:15 -0300 Subject: [PATCH] feat: Prometheus integration (#35) * refactor: move Prometheus integration to extension * chore: update devenv to include Grafana provisioning config for Prometheus * chore: update config for PromEx * feat: add Telemetry events * feat: add promex plugin * feat: add Grafana dashboard for deliveries --- devenv.lock | 49 +- devenv.nix | 22 + prometheus.yml | 5 +- tololo/config/config.exs | 2 +- tololo/config/dev.exs | 2 +- tololo/config/prod.exs | 2 - tololo/config/test.exs | 1 - tololo/core/lib/deliveries/delivery.ex | 6 + tololo/core/lib/deliveries/update_history.ex | 7 + tololo/core/lib/extension.ex | 21 + tololo/extensions/prometheus/.gitignore | 23 + tololo/extensions/prometheus/lib/extension.ex | 44 ++ .../prometheus/lib/promex.ex} | 17 +- .../prometheus/lib/promex_plugin.ex | 67 ++ tololo/extensions/prometheus/mix.exs | 30 + tololo/extensions/prometheus/mix.lock | 71 +++ .../priv/dashboards/deliveries.json.eex | 595 ++++++++++++++++++ .../prometheus/test/prometheus/handler.exs | 8 + .../prometheus/test/test_helper.exs | 1 + .../extensions/telegram_bot/lib/ash/user.ex | 34 +- .../extensions/telegram_bot/lib/extension.ex | 6 + .../telegram_bot/lib/telegram/chains/done.ex | 2 + tololo/lib/tololo/application.ex | 2 +- tololo/lib/tololo_web.ex | 4 +- tololo/lib/tololo_web/endpoint.ex | 2 +- tololo/lib/tololo_web/telemetry.ex | 3 + tololo/mix.exs | 5 +- 27 files changed, 982 insertions(+), 49 deletions(-) create mode 100644 tololo/extensions/prometheus/.gitignore create mode 100644 tololo/extensions/prometheus/lib/extension.ex rename tololo/{lib/tololo/prometheus/prometheus.ex => extensions/prometheus/lib/promex.ex} (56%) create mode 100644 tololo/extensions/prometheus/lib/promex_plugin.ex create mode 100644 tololo/extensions/prometheus/mix.exs create mode 100644 tololo/extensions/prometheus/mix.lock create mode 100644 tololo/extensions/prometheus/priv/dashboards/deliveries.json.eex create mode 100644 tololo/extensions/prometheus/test/prometheus/handler.exs create mode 100644 tololo/extensions/prometheus/test/test_helper.exs diff --git a/devenv.lock b/devenv.lock index c6c4140d..4e80bfcc 100644 --- a/devenv.lock +++ b/devenv.lock @@ -31,10 +31,31 @@ "type": "github" } }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1737465171, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ - "pre-commit-hooks", + "git-hooks", "nixpkgs" ] }, @@ -81,33 +102,15 @@ "type": "github" } }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": "flake-compat", - "gitignore": "gitignore", - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1735882644, - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "a5a961387e75ae44cc20f0a57ae463da5e959656", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "root": { "inputs": { "devenv": "devenv", + "git-hooks": "git-hooks", "nixpkgs": "nixpkgs", "nixpkgs-unstable": "nixpkgs-unstable", - "pre-commit-hooks": "pre-commit-hooks" + "pre-commit-hooks": [ + "git-hooks" + ] } } }, diff --git a/devenv.nix b/devenv.nix index 09d33006..1e9af171 100644 --- a/devenv.nix +++ b/devenv.nix @@ -8,6 +8,23 @@ let printf $((RANDOM % 10000)) > $out ''); localtunnel-subdomain = "tololo-" + randomSuffix; + grafana-provisioning = pkgs.runCommand "grafana-provisioning" { + buildInputs = [ pkgs.jq ]; + } '' + mkdir -p $out/datasources + cat > $out/datasources/prometheus.yml <` to any timeseries scraped from this config. - - job_name: "prometheus" + - job_name: 'tololo' # metrics_path defaults to '/metrics' # scheme defaults to 'http'. static_configs: - - targets: ["localhost:9090"] + - targets: ["localhost:9090", "localhost:4000"] diff --git a/tololo/config/config.exs b/tololo/config/config.exs index 1c124e21..8330b6d7 100644 --- a/tololo/config/config.exs +++ b/tololo/config/config.exs @@ -49,7 +49,7 @@ config :spark, config :tololo, ecto_repos: [Tololo.Repo], generators: [timestamp_type: :utc_datetime], - extensions: [Tololo.Extensions.TelegramBot], + extensions: [Tololo.Extensions.TelegramBot, Tololo.Extensions.Prometheus], pubsub: Tololo.PubSub, geocoding_endpoint: "https://nominatim.openstreetmap.org/search", business_name: "Sushi", diff --git a/tololo/config/dev.exs b/tololo/config/dev.exs index 45fe6641..a5ad275f 100644 --- a/tololo/config/dev.exs +++ b/tololo/config/dev.exs @@ -31,7 +31,7 @@ config :tololo, TololoWeb.Endpoint, config :tololo, :kafka_driver, driver: Tololo.Kafka.Driver -config :tololo, Tololo.Prometheus, +config :tololo, Tololo.Extensions.Prometheus.PromEx, disabled: false, manual_metrics_start_delay: :no_delay, drop_metrics_groups: [], diff --git a/tololo/config/prod.exs b/tololo/config/prod.exs index 2af3b2da..e9180275 100644 --- a/tololo/config/prod.exs +++ b/tololo/config/prod.exs @@ -18,8 +18,6 @@ config :logger, level: :info config :tololo, :kafka_driver, driver: Tololo.Kafka.Noop -config :tololo, Tololo.Prometheus, disabled: true - config :kafka_ex, brokers: [ {"localhost", 9092}, diff --git a/tololo/config/test.exs b/tololo/config/test.exs index 16870a32..13ad6de2 100644 --- a/tololo/config/test.exs +++ b/tololo/config/test.exs @@ -39,7 +39,6 @@ config :phoenix_live_view, enable_expensive_runtime_checks: true config :tololo, :kafka_driver, driver: Tololo.Kafka.Noop -config :tololo, Tololo.Prometheus, disabled: true config :kafka_ex, brokers: [ diff --git a/tololo/core/lib/deliveries/delivery.ex b/tololo/core/lib/deliveries/delivery.ex index d342696f..14398eea 100644 --- a/tololo/core/lib/deliveries/delivery.ex +++ b/tololo/core/lib/deliveries/delivery.ex @@ -247,6 +247,12 @@ defmodule TololoCore.Deliveries.Delivery do comment ) + :telemetry.execute([:ash, :deliveries, :update, :state], %{count: 1}, %{ + action: :done_with_distance_check, + old_state: old_state, + new_state: new_state + }) + {:ok, result} _changeset, error -> diff --git a/tololo/core/lib/deliveries/update_history.ex b/tololo/core/lib/deliveries/update_history.ex index a67d3ec1..48fbf1b0 100644 --- a/tololo/core/lib/deliveries/update_history.ex +++ b/tololo/core/lib/deliveries/update_history.ex @@ -22,6 +22,13 @@ defmodule TololoCore.Deliveries.UpdateHistory do |> Ash.Changeset.after_transaction(fn _changeset, {:ok, result} -> DeliveryStateChanges.add_to_state_history!(id, old_state, new_state, comment) + + :telemetry.execute([:ash, :deliveries, :update, :state], %{count: 1}, %{ + action: :update_state, + old_state: old_state, + new_state: new_state + }) + {:ok, result} _changeset, error -> diff --git a/tololo/core/lib/extension.ex b/tololo/core/lib/extension.ex index 1cd26ecc..2b9994cf 100644 --- a/tololo/core/lib/extension.ex +++ b/tololo/core/lib/extension.ex @@ -3,7 +3,28 @@ defmodule TololoCore.Extension do Defines the extension behaviour. """ @callback routes() :: Macro.t() + @callback endpoint() :: Macro.t() @callback ash_domains() :: [Ash.Domain.t()] @callback child_spec(any()) :: Supervisor.child_spec() # @callback payment() + + @extensions Application.compile_env(:tololo, :extensions) + + defmacro __using__(:routes) do + quote do + unquote( + @extensions + |> Enum.map(fn extension_module -> extension_module.routes() end) + ) + end + end + + defmacro __using__(:endpoint) do + quote do + unquote( + @extensions + |> Enum.map(fn extension_module -> extension_module.endpoint() end) + ) + end + end end diff --git a/tololo/extensions/prometheus/.gitignore b/tololo/extensions/prometheus/.gitignore new file mode 100644 index 00000000..02e3495b --- /dev/null +++ b/tololo/extensions/prometheus/.gitignore @@ -0,0 +1,23 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +tololo_extension_telegram_bot-*.tar + +# Temporary files, for example, from tests. +/tmp/ diff --git a/tololo/extensions/prometheus/lib/extension.ex b/tololo/extensions/prometheus/lib/extension.ex new file mode 100644 index 00000000..e15efd69 --- /dev/null +++ b/tololo/extensions/prometheus/lib/extension.ex @@ -0,0 +1,44 @@ +defmodule Tololo.Extensions.Prometheus do + @moduledoc false + use Supervisor + @behaviour TololoCore.Extension + + @impl true + def child_spec(init_arg) do + %{ + id: __MODULE__, + start: {__MODULE__, :start_link, [init_arg]}, + type: :supervisor, + restart: :permanent + } + end + + def start_link(_init_arg) do + Supervisor.start_link(__MODULE__, nil, name: __MODULE__) + end + + @impl true + def init(_) do + children = [ + Tololo.Extensions.Prometheus.PromEx + ] + + Supervisor.init(children, strategy: :one_for_all) + end + + @impl true + def routes() do + quote do + end + end + + @impl true + def endpoint() do + quote do + plug PromEx.Plug, prom_ex_module: Tololo.Extensions.Prometheus.PromEx + end + end + + @impl true + def ash_domains(), do: [] +end diff --git a/tololo/lib/tololo/prometheus/prometheus.ex b/tololo/extensions/prometheus/lib/promex.ex similarity index 56% rename from tololo/lib/tololo/prometheus/prometheus.ex rename to tololo/extensions/prometheus/lib/promex.ex index 93f0e8db..31085bc2 100644 --- a/tololo/lib/tololo/prometheus/prometheus.ex +++ b/tololo/extensions/prometheus/lib/promex.ex @@ -1,4 +1,4 @@ -defmodule Tololo.Prometheus do +defmodule Tololo.Extensions.Prometheus.PromEx do @moduledoc """ PromEx module, handles integration with Prometheus. """ @@ -15,18 +15,15 @@ defmodule Tololo.Prometheus do Plugins.Beam, {Plugins.Phoenix, router: TololoWeb.Router, endpoint: TololoWeb.Endpoint}, Plugins.Ecto, - Plugins.PhoenixLiveView - - # Add your own PromEx metrics plugins - # Tololo.Users.PromExPlugin - # TODO: implement plugin for metrics described in https://github.com/ElixirCL/tololo/issues/1#issuecomment-2582911263 + Plugins.PhoenixLiveView, + Tololo.Extensions.Prometheus.PromExPlugin ] end @impl true def dashboard_assigns do [ - datasource_id: "prometheus", + datasource_id: "prometheus-tololo", default_selected_interval: "30s" ] end @@ -39,11 +36,9 @@ defmodule Tololo.Prometheus do {:prom_ex, "beam.json"}, {:prom_ex, "phoenix.json"}, {:prom_ex, "ecto.json"}, - {:prom_ex, "phoenix_live_view.json"} + {:prom_ex, "phoenix_live_view.json"}, - # Add your dashboard definitions here with the format: {:otp_app, "path_in_priv"} - # {:tololo, "/grafana_dashboards/user_metrics.json"} - # TODO: add dashboards for custom Tololo metrics + {:tololo_extension_prometheus, "/dashboards/deliveries.json"} ] end end diff --git a/tololo/extensions/prometheus/lib/promex_plugin.ex b/tololo/extensions/prometheus/lib/promex_plugin.ex new file mode 100644 index 00000000..787bcbfe --- /dev/null +++ b/tololo/extensions/prometheus/lib/promex_plugin.ex @@ -0,0 +1,67 @@ +defmodule Tololo.Extensions.Prometheus.PromExPlugin do + use PromEx.Plugin + + @impl true + def event_metrics(_opts) do + Event.build( + :tololo_deliveries, + [ + counter( + [:ash, :deliveries, :create, :count], + event_name: [:ash, :deliveries, :create, :stop], + description: "Total number of delivery creations", + measurement: :count, + tags: [:action], + tag_values: fn metadata -> + %{ + action: metadata.action + } + end + ), + counter( + [:ash, :deliveries, :update, :state, :count], + event_name: [:ash, :deliveries, :update, :stop], + description: "Total number of state changes", + measurement: :count, + tags: [:action], + tag_values: fn metadata -> + %{ + action: metadata.action + } + end + ), + counter( + [:ash, :deliveries, :update, :state, :individual, :count], + event_name: [:ash, :deliveries, :update, :state], + description: "Total number of state changes for individual state", + measurement: :count, + tags: [:action, :state], + tag_values: fn metadata -> + %{ + action: metadata.action, + state: metadata.new_state + } + end + ), + counter( + [:ash, :users, :approve, :count], + event_name: [:ash, :users, :approve], + description: "Total number of approved users", + measurement: :count + ), + counter( + [:ash, :users, :delivery, :assigned, :count], + event_name: [:ash, :users, :delivery, :assigned], + description: "Total number of assigned deliveries to users", + measurement: :count + ), + counter( + [:ash, :users, :delivery, :done, :count], + event_name: [:ash, :users, :delivery, :done], + description: "Total number of deliveries marked as done by users", + measurement: :count + ) + ] + ) + end +end diff --git a/tololo/extensions/prometheus/mix.exs b/tololo/extensions/prometheus/mix.exs new file mode 100644 index 00000000..a732e0db --- /dev/null +++ b/tololo/extensions/prometheus/mix.exs @@ -0,0 +1,30 @@ +defmodule Tololo.Extensions.Prometheus.MixProject do + use Mix.Project + + def project do + [ + app: :tololo_extension_prometheus, + version: "0.1.0", + elixir: "~> 1.18", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + def application do + [ + extra_applications: [:logger] + ] + end + + defp deps do + [ + {:phoenix, "~> 1.7.18"}, + {:prom_ex, "~> 1.11.0"}, + {:tololo_core, + path: + Path.join(["..", "..", "core"]) + |> Path.expand()} + ] + end +end diff --git a/tololo/extensions/prometheus/mix.lock b/tololo/extensions/prometheus/mix.lock new file mode 100644 index 00000000..2e40fd61 --- /dev/null +++ b/tololo/extensions/prometheus/mix.lock @@ -0,0 +1,71 @@ +%{ + "absinthe": {:hex, :absinthe, "1.7.8", "43443d12ad2b4fcce60e257ac71caf3081f3d5c4ddd5eac63a02628bcaf5b556", [:mix], [{:dataloader, "~> 1.0.0 or ~> 2.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:opentelemetry_process_propagator, "~> 0.2.1 or ~> 0.3", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c4085df201892a498384f997649aedb37a4ce8a726c170d5b5617ed3bf45d40b"}, + "absinthe_plug": {:hex, :absinthe_plug, "1.5.8", "38d230641ba9dca8f72f1fed2dfc8abd53b3907d1996363da32434ab6ee5d6ab", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bbb04176647b735828861e7b2705465e53e2cf54ccf5a73ddd1ebd855f996e5a"}, + "ash": {:hex, :ash, "3.4.62", "ef41463b12095fe9566962793f4e4d94c2753e1c3bfc79acc09471cc1efc7f51", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.4.8 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.11", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.29 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, ">= 0.2.6 and < 1.0.0-0", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1780ec2029353213d5a35b1950a53da7225bce756f0be05e35708330f51d9cf0"}, + "ash_admin": {:hex, :ash_admin, "0.12.6", "ffebd80dd4af1b14aa85d589e40cdb94d6751db207e3c39634cf744a51c83f7d", [:mix], [{:ash, ">= 3.4.47 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_phoenix, ">= 2.1.8 and < 3.0.0-0", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:gettext, "~> 0.26", [hex: :gettext, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}], "hexpm", "15ea0c8a73b897c3c81300913b9e03fa162b7796e458c550869820bf39fa39ed"}, + "ash_authentication": {:hex, :ash_authentication, "4.4.8", "6d5d6f8948f7df2ea70429e96becbdd5af41e17db547264222fe0056256ee657", [:mix], [{:ash, ">= 3.4.29 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_postgres, "~> 2.0", [hex: :ash_postgres, repo: "hexpm", optional: true]}, {:assent, "~> 0.2.13", [hex: :assent, repo: "hexpm", optional: false]}, {:bcrypt_elixir, "~> 3.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: false]}, {:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:finch, "~> 0.19", [hex: :finch, repo: "hexpm", optional: false]}, {:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:joken, "~> 2.5", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}], "hexpm", "d2aa13af9a2e56ca564f2971ede8efc7d2ad5543e6784a187b54c0153ed32d29"}, + "ash_authentication_phoenix": {:hex, :ash_authentication_phoenix, "2.4.5", "867a7df1af6d09ce6eb28485f66b5c96a8efad61e7926d39537bd2591f71da0c", [:mix], [{:ash, "~> 3.0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_authentication, "~> 4.1", [hex: :ash_authentication, repo: "hexpm", optional: false]}, {:ash_phoenix, "~> 2.0", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:bcrypt_elixir, "~> 3.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: false]}, {:gettext, "~> 0.26", [hex: :gettext, repo: "hexpm", optional: true]}, {:igniter, ">= 0.5.1 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_html_helpers, "~> 1.0", [hex: :phoenix_html_helpers, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:slugify, "~> 1.3", [hex: :slugify, repo: "hexpm", optional: false]}], "hexpm", "64fe265d656de4296904b408cb019ffc156fabb4be5f6894e507bb05f346ee3c"}, + "ash_graphql": {:hex, :ash_graphql, "1.5.1", "241c691bcc9171f78f31e2727495700be0e3c2ae56affd4f08b7bf84af2a6f4a", [:mix], [{:absinthe, "~> 1.7", [hex: :absinthe, repo: "hexpm", optional: false]}, {:absinthe_phoenix, "~> 2.0.0", [hex: :absinthe_phoenix, repo: "hexpm", optional: true]}, {:absinthe_plug, "~> 1.4", [hex: :absinthe_plug, repo: "hexpm", optional: false]}, {:ash, ">= 3.2.3 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:igniter, ">= 0.3.34 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:spark, ">= 2.2.10 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "0e0a9f8cb32e517364f08645eaed589f2185410ddaaa11a9af52ee0e7137147a"}, + "ash_phoenix": {:hex, :ash_phoenix, "2.1.17", "69412d0fb21cfd0fd3212f21c6a58c52e7677a3685f594901e0062ffcf1a5570", [:mix], [{:ash, ">= 3.4.31 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:igniter, ">= 0.4.3 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3 or ~> 1.0-rc.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:spark, ">= 2.2.29 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "bb2df5f69cb9a042478bd867cf14ed33428da007de360a437848c3fd7f0de473"}, + "ash_postgres": {:hex, :ash_postgres, "2.5.1", "9366f69a41a1bbc5e3e898ff3a0bff37ad8aa23cf6bdc90722c2357042f3db79", [:mix], [{:ash, ">= 3.4.48 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_sql, ">= 0.2.43 and < 1.0.0-0", [hex: :ash_sql, repo: "hexpm", optional: false]}, {:ecto, ">= 3.12.1 and < 4.0.0-0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.12", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:igniter, ">= 0.5.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "b8e44dc421091fd90404f4c592cd0032b4039660e1e51e5ecf44a062a09ff21d"}, + "ash_sql": {:hex, :ash_sql, "0.2.52", "8ca8858f85a4d9a4dcc05deb9f2116b92f939e006e5907e8f97998dfb8d5ff04", [:mix], [{:ash, ">= 3.4.60 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}], "hexpm", "c17207c7169c8ba1d8b5e705be944160b2ad74a780fdb9442202bd1afd6e4549"}, + "assent": {:hex, :assent, "0.2.13", "11226365d2d8661d23e9a2cf94d3255e81054ff9d88ac877f28bfdf38fa4ef31", [:mix], [{:certifi, ">= 0.0.0", [hex: :certifi, repo: "hexpm", optional: true]}, {:finch, "~> 0.15", [hex: :finch, repo: "hexpm", optional: true]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: true]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:req, "~> 0.4", [hex: :req, repo: "hexpm", optional: true]}, {:ssl_verify_fun, ">= 0.0.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: true]}], "hexpm", "bf9f351b01dd6bceea1d1f157f05438f6765ce606e6eb8d29296003d29bf6eab"}, + "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.2.1", "e361261a0401d82dadc1ab7b969f91d250bf7577283e933fe8c5b72f8f5b3c46", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "81170177d5c2e280d12141a0b9d9e299bf731535e2d959982bdcd4cfe3c82865"}, + "castore": {:hex, :castore, "1.0.11", "4bbd584741601eb658007339ea730b082cc61f3554cf2e8f39bf693a11b49073", [:mix], [], "hexpm", "e03990b4db988df56262852f20de0f659871c35154691427a5047f4967a16a62"}, + "cldr_utils": {:hex, :cldr_utils, "2.28.2", "f500667164a9043369071e4f9dcef31f88b8589b2e2c07a1eb9f9fa53cb1dce9", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "c506eb1a170ba7cdca59b304ba02a56795ed119856662f6b1a420af80ec42551"}, + "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, + "comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"}, + "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, + "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, + "digital_token": {:hex, :digital_token, "1.0.0", "454a4444061943f7349a51ef74b7fb1ebd19e6a94f43ef711f7dae88c09347df", [:mix], [{:cldr_utils, "~> 2.17", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "8ed6f5a8c2fa7b07147b9963db506a1b4c7475d9afca6492136535b064c9e9e6"}, + "ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"}, + "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, + "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, + "ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"}, + "ex_cldr": {:hex, :ex_cldr, "2.40.2", "1361d06624d7533fb3a2df59c82c7108b18ef55e884f48d503597fce4ce28d97", [:mix], [{:cldr_utils, "~> 2.28", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: true]}], "hexpm", "cd9039ca9a7c61b99c053a16bd2201ebd7d1c87b49499a4c6d761ec14bca4442"}, + "ex_cldr_calendars": {:hex, :ex_cldr_calendars, "1.26.4", "e29c40bed4be6202f9333474e5bb3cfb51bccfb93547b1302cf91ea1140383ab", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_numbers, "~> 2.31", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.16", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.21", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6538ee5328ddf5457b5f11bfccb465405d0d7011ac0bb24d826af0be00335170"}, + "ex_cldr_currencies": {:hex, :ex_cldr_currencies, "2.16.3", "1ec6444b5d0c0aabba5a3bc321d73f1c9c751c6add92e7fb7775ccc071d96bd8", [:mix], [{:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "4d1b5f8449fdf0ece6a2e5c7401ad8fcfde77ee6ea480bddc16e266dfa2b570c"}, + "ex_cldr_dates_times": {:hex, :ex_cldr_dates_times, "2.21.0", "f6a28359252941703387f540ceab4c0173893b431f22d0efecdb451d36634a12", [:mix], [{:calendar_interval, "~> 0.2", [hex: :calendar_interval, repo: "hexpm", optional: true]}, {:ex_cldr, "~> 2.40", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_calendars, "~> 1.25 or ~> 2.0", [hex: :ex_cldr_calendars, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.17", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:tz, "~> 0.26", [hex: :tz, repo: "hexpm", optional: true]}], "hexpm", "225f1dcbcfa91134d84c33686bc428e18b6dbc7d02560e936d3304536000e040"}, + "ex_cldr_lists": {:hex, :ex_cldr_lists, "2.11.1", "ad18f861d7c5ca82aac6d173469c6a2339645c96790172ab0aa255b64fb7303b", [:mix], [{:ex_cldr_numbers, "~> 2.25", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_doc, "~> 0.18", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "00161c04510ccb3f18b19a6b8562e50c21f1e9c15b8ff4c934bea5aad0b4ade2"}, + "ex_cldr_messages": {:hex, :ex_cldr_messages, "1.0.2", "9909829e8cdb4eeb6d5b4dbe76b8e07ae39d2d2254fb943ff74dd9ace55a9120", [:mix], [{:ex_cldr_dates_times, "~> 2.13", [hex: :ex_cldr_dates_times, repo: "hexpm", optional: true]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: true]}, {:ex_cldr_numbers, "~> 2.28", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_cldr_units, "~> 3.12", [hex: :ex_cldr_units, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.20", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:ex_money, "~> 5.9", [hex: :ex_money, repo: "hexpm", optional: true]}, {:gettext, "~> 0.19", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "3805a92f5ff64ee951f3a0174a85221af0117276ac794638df25b845edf431fa"}, + "ex_cldr_numbers": {:hex, :ex_cldr_numbers, "2.33.5", "6cd39c549019562f83349d5aba1dcaa40d8ea36ed0e92b0d03dff65231a2eb40", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:digital_token, "~> 0.3 or ~> 1.0", [hex: :digital_token, repo: "hexpm", optional: false]}, {:ex_cldr, "~> 2.38", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:ex_cldr_currencies, "~> 2.16", [hex: :ex_cldr_currencies, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "1f0baf18ebdec2016018544fc92c8280722e7a6c90aad08b047b00d55594c9b4"}, + "ex_cldr_units": {:hex, :ex_cldr_units, "3.17.2", "b0483d5c61c6c8649aafdcafc7372dd71a7a30f52dd4c9b072576467bf721454", [:mix], [{:cldr_utils, "~> 2.25", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ex_cldr_lists, "~> 2.10", [hex: :ex_cldr_lists, repo: "hexpm", optional: false]}, {:ex_cldr_numbers, "~> 2.33.0", [hex: :ex_cldr_numbers, repo: "hexpm", optional: false]}, {:ex_doc, "~> 0.18", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "457d76c6e3b548bd7aba3c7b5d157213be2842d1162c2283abf81d9e2f1e1fc7"}, + "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, + "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, + "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"}, + "hpax": {:hex, :hpax, "1.0.2", "762df951b0c399ff67cc57c3995ec3cf46d696e41f0bba17da0518d94acd4aac", [:mix], [], "hexpm", "2f09b4c1074e0abd846747329eaa26d535be0eb3d189fa69d812bfb8bfefd32f"}, + "iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, + "joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"}, + "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, + "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, + "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"}, + "multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"}, + "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, + "owl": {:hex, :owl, "0.12.1", "d3146087315c4528ee32411495ba10ec88102597b638d4d1455cf9d245dfb57a", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "d7eb9746aa89c40c46b479d6c2a70b82b94993520e40f21d0b09654f23eebf35"}, + "phoenix": {:hex, :phoenix, "1.7.19", "36617efe5afbd821099a8b994ff4618a340a5bfb25531a1802c4d4c634017a57", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "ba4dc14458278773f905f8ae6c2ec743d52c3a35b6b353733f64f02dfe096cd6"}, + "phoenix_html": {:hex, :phoenix_html, "4.2.0", "83a4d351b66f472ebcce242e4ae48af1b781866f00ef0eb34c15030d4e2069ac", [:mix], [], "hexpm", "9713b3f238d07043583a94296cc4bbdceacd3b3a6c74667f4df13971e7866ec8"}, + "phoenix_html_helpers": {:hex, :phoenix_html_helpers, "1.0.1", "7eed85c52eff80a179391036931791ee5d2f713d76a81d0d2c6ebafe1e11e5ec", [:mix], [{:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cffd2385d1fa4f78b04432df69ab8da63dc5cf63e07b713a4dcf36a3740e3090"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.4", "327491b033e79db2f887b065c5a2993228449091883d74cfa1baa12f8c98d5eb", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a9865316ddf8d78f382d63af278d20436b52d262b60239956817a61279514366"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, + "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, + "phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"}, + "picosat_elixir": {:hex, :picosat_elixir, "0.2.3", "bf326d0f179fbb3b706bb2c15fbc367dacfa2517157d090fdfc32edae004c597", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f76c9db2dec9d2561ffaa9be35f65403d53e984e8cd99c832383b7ab78c16c66"}, + "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, + "postgrex": {:hex, :postgrex, "0.20.0", "363ed03ab4757f6bc47942eff7720640795eb557e1935951c1626f0d303a3aed", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "d36ef8b36f323d29505314f704e21a1a038e2dc387c6409ee0cd24144e187c0f"}, + "reactor": {:hex, :reactor, "0.12.1", "8bc7b0547c5ada64c9c16ef55f598cea9037d19810c59709d5cede33c5e5b562", [:mix], [{:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1f26550b4079d46a1b84a153d7427990c2e4adf3dfdbf6ca04c2ea0a9bf1d24d"}, + "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, + "slugify": {:hex, :slugify, "1.3.1", "0d3b8b7e5c1eeaa960e44dce94382bee34a39b3ea239293e457a9c5b47cc6fd3", [:mix], [], "hexpm", "cb090bbeb056b312da3125e681d98933a360a70d327820e4b7f91645c4d8be76"}, + "spark": {:hex, :spark, "2.2.45", "19e3a879e80d02853ded85ed7b4c0a84a5d2e395f9d0c884e1a13afbe026929d", [:mix], [{:igniter, ">= 0.3.64 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: true]}], "hexpm", "70b272d0ee16e3c10a4f8cf0ef6152840828152e68f2f8e3046e89567f2b49ad"}, + "splode": {:hex, :splode, "0.2.8", "289d4eec13e7a83061bc44827877eb4c575e1fdf198bd1a9c6449f9b64805059", [:mix], [], "hexpm", "dbe92fa526589416435e12203b56db1f74c834d207bc474016cedf930d987284"}, + "stream_data": {:hex, :stream_data, "1.1.2", "05499eaec0443349ff877aaabc6e194e82bda6799b9ce6aaa1aadac15a9fdb4d", [:mix], [], "hexpm", "129558d2c77cbc1eb2f4747acbbea79e181a5da51108457000020a906813a1a9"}, + "telegex": {:hex, :telegex, "1.8.0", "982ef33e9576167189c4980c27ebe927e8b0945d0e8c93c1173ea9482ef78137", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:typed_struct, "~> 0.3.0", [hex: :typed_struct, repo: "hexpm", optional: false]}], "hexpm", "899fdadedc3691faf923e12639247b083f259284b2963e94b265088473c7349b"}, + "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, + "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, + "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"}, +} diff --git a/tololo/extensions/prometheus/priv/dashboards/deliveries.json.eex b/tololo/extensions/prometheus/priv/dashboards/deliveries.json.eex new file mode 100644 index 00000000..92e106f3 --- /dev/null +++ b/tololo/extensions/prometheus/priv/dashboards/deliveries.json.eex @@ -0,0 +1,595 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 12, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus-tololo" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prometheus-tololo" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(ash_deliveries_create_count{action=\"initialize\"}[1m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Delivery Creation Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus-tololo" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "editorMode": "code", + "expr": "rate(ash_users_delivery_done_count[1m])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Completed User Deliveries", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus-tololo" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "editorMode": "code", + "expr": "rate(ash_deliveries_update_state_count[1m])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "State Change Operations", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus-tololo" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "editorMode": "code", + "expr": "rate(ash_users_delivery_assigned_count[1m])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Delivery Assignments to Users", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus-tololo" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 13 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "editorMode": "code", + "expr": "sum by (state) (rate(ash_deliveries_update_state_individual_count[1m]))", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "State Transitions Breakdown (per state)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prometheus-tololo" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 13 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.1", + "targets": [ + { + "editorMode": "code", + "expr": "rate(ash_users_approve_count[1m])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "User Approvals Over Time", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Tololo - Deliveries", + "uid": "beddaa6r6ayo0b", + "version": 16, + "weekStart": "" +} diff --git a/tololo/extensions/prometheus/test/prometheus/handler.exs b/tololo/extensions/prometheus/test/prometheus/handler.exs new file mode 100644 index 00000000..f17c132f --- /dev/null +++ b/tololo/extensions/prometheus/test/prometheus/handler.exs @@ -0,0 +1,8 @@ +defmodule Tololo.Extensions.PrometheusTest do + use ExUnit.Case + doctest TololoExtensionPrometheusBot + + # test "greets the world" do + # assert TololoExtensionTelegramBot.hello() == :world + # end +end diff --git a/tololo/extensions/prometheus/test/test_helper.exs b/tololo/extensions/prometheus/test/test_helper.exs new file mode 100644 index 00000000..869559e7 --- /dev/null +++ b/tololo/extensions/prometheus/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/tololo/extensions/telegram_bot/lib/ash/user.ex b/tololo/extensions/telegram_bot/lib/ash/user.ex index 63ef596d..309f4c65 100644 --- a/tololo/extensions/telegram_bot/lib/ash/user.ex +++ b/tololo/extensions/telegram_bot/lib/ash/user.ex @@ -23,7 +23,7 @@ defmodule Tololo.Extensions.TelegramBot.Ash.User do end actions do - defaults [:read, :destroy, :create, :update] + defaults [:read, :destroy, :create] default_accept [:id, :status, :deliveries_id] @@ -31,6 +31,26 @@ defmodule Tololo.Extensions.TelegramBot.Ash.User do filter expr(status == :allowed) end + update :update do + primary? true + require_atomic? false + + change fn changeset, _context -> + Ash.Changeset.after_transaction(changeset, fn + _changeset, {:ok, %{status: :allowed} = result} -> + :telemetry.execute([:ash, :users, :approve], %{count: 1}) + + {:ok, result} + + _changeset, {:ok, result} -> + {:ok, result} + + _changeset, error -> + error + end) + end + end + update :add_deliveries do require_atomic? false @@ -47,6 +67,18 @@ defmodule Tololo.Extensions.TelegramBot.Ash.User do data.deliveries_id ++ arguments.deliveries ) end + + change fn changeset, _context -> + Ash.Changeset.after_transaction(changeset, fn + _changeset, {:ok, result} -> + :telemetry.execute([:ash, :users, :delivery, :assigned], %{count: 1}) + + {:ok, result} + + _changeset, error -> + error + end) + end end end diff --git a/tololo/extensions/telegram_bot/lib/extension.ex b/tololo/extensions/telegram_bot/lib/extension.ex index 089e8d31..34096b07 100644 --- a/tololo/extensions/telegram_bot/lib/extension.ex +++ b/tololo/extensions/telegram_bot/lib/extension.ex @@ -45,6 +45,12 @@ defmodule Tololo.Extensions.TelegramBot do end end + @impl true + def endpoint() do + quote do + end + end + @impl true def ash_domains(), do: [Tololo.Extensions.TelegramBot.Ash.Users] end diff --git a/tololo/extensions/telegram_bot/lib/telegram/chains/done.ex b/tololo/extensions/telegram_bot/lib/telegram/chains/done.ex index eff59f35..9614cf4e 100644 --- a/tololo/extensions/telegram_bot/lib/telegram/chains/done.ex +++ b/tololo/extensions/telegram_bot/lib/telegram/chains/done.ex @@ -131,6 +131,8 @@ defmodule Tololo.Extensions.TelegramBot.Done do |> Delivery.done_with_distance_check!(nil, actor: @actor) if new_state == "Delivery_Done" do + :telemetry.execute([:ash, :users, :delivery, :done], %{count: 1}) + gettext(""" Delivery successfully marked as done. """) diff --git a/tololo/lib/tololo/application.ex b/tololo/lib/tololo/application.ex index 80a66eb5..4909aed7 100644 --- a/tololo/lib/tololo/application.ex +++ b/tololo/lib/tololo/application.ex @@ -23,7 +23,7 @@ defmodule Tololo.Application do children = [ - Tololo.Prometheus, + # Tololo.Prometheus, TololoWeb.Telemetry, Tololo.Repo, {DNSCluster, query: Application.get_env(:tololo, :dns_cluster_query) || :ignore}, diff --git a/tololo/lib/tololo_web.ex b/tololo/lib/tololo_web.ex index 4dfc6ce3..3805e9ea 100644 --- a/tololo/lib/tololo_web.ex +++ b/tololo/lib/tololo_web.ex @@ -17,8 +17,6 @@ defmodule TololoWeb do those modules here. """ - @extensions Application.compile_env(:tololo, :extensions) - def static_paths, do: ~w(assets fonts images favicon.ico robots.txt) def router do @@ -31,7 +29,7 @@ defmodule TololoWeb do import Phoenix.LiveView.Router # inject routes from extension modules - unquote(@extensions |> Enum.map(fn extension_module -> extension_module.routes() end)) + use TololoCore.Extension, :routes end end diff --git a/tololo/lib/tololo_web/endpoint.ex b/tololo/lib/tololo_web/endpoint.ex index 7e3c7e30..a0d33a73 100644 --- a/tololo/lib/tololo_web/endpoint.ex +++ b/tololo/lib/tololo_web/endpoint.ex @@ -40,7 +40,7 @@ defmodule TololoWeb.Endpoint do plug Plug.RequestId - plug PromEx.Plug, prom_ex_module: Tololo.Prometheus + use TololoCore.Extension, :endpoint plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] diff --git a/tololo/lib/tololo_web/telemetry.ex b/tololo/lib/tololo_web/telemetry.ex index 8b8d9261..4c9a7ba3 100644 --- a/tololo/lib/tololo_web/telemetry.ex +++ b/tololo/lib/tololo_web/telemetry.ex @@ -73,6 +73,9 @@ defmodule TololoWeb.Telemetry do description: "The time the connection spent waiting before being checked out for the query" ), + counter("ash.deliveries.update.state.count", + tags: [:action, :old_state, :new_state] + ), # VM Metrics summary("vm.memory.total", unit: {:byte, :kilobyte}), diff --git a/tololo/mix.exs b/tololo/mix.exs index 27c361a6..9067ea31 100644 --- a/tololo/mix.exs +++ b/tololo/mix.exs @@ -70,7 +70,6 @@ defmodule Tololo.MixProject do {:bandit, "~> 1.5"}, {:kafka_ex, "~> 0.11"}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, - {:prom_ex, "~> 1.11.0"}, {:ex_doc, "~> 0.36", only: [:dev, :test], runtime: false}, {:opentelemetry_exporter, "~> 1.8.0"}, {:opentelemetry, "~> 1.5.0"}, @@ -89,6 +88,10 @@ defmodule Tololo.MixProject do path: Path.join(["extensions", "telegram_bot"]) |> Path.expand()}, + {:tololo_extension_prometheus, + path: + Path.join(["extensions", "prometheus"]) + |> Path.expand()}, {:tololo_core, path: Path.join(["core"])