From a6ed9ada387b7a5d425b5ad464bc3327bf30d0c6 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sat, 9 Nov 2024 13:45:35 +0100 Subject: [PATCH 01/13] Breakdown of jobs by state --- .formatter.exs | 4 +- lib/oban/live_dashboard.ex | 212 +++++++++++++++++++++++++++---------- 2 files changed, 160 insertions(+), 56 deletions(-) diff --git a/.formatter.exs b/.formatter.exs index d2cda26..2395471 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,4 +1,6 @@ # Used by "mix format" [ - inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], + # Need to increase it because of <.label_value_list /> that does a
 tag, which will look broken with extra whitespace.
+  heex_line_length: 300
 ]
diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex
index e4bffbb..88b3723 100644
--- a/lib/oban/live_dashboard.ex
+++ b/lib/oban/live_dashboard.ex
@@ -4,43 +4,46 @@ defmodule Oban.LiveDashboard do
   import Phoenix.LiveDashboard.Helpers, only: [format_value: 2]
   import Ecto.Query
 
-  @impl true
-  def menu_link(_, _) do
-    {:ok, "Oban"}
-  end
+  @per_page_limits [20, 50, 100]
+
+  @oban_sorted_job_states [
+    "executing",
+    "available",
+    "scheduled",
+    "retryable",
+    "cancelled",
+    "discarded",
+    "completed"
+  ]
 
   @impl true
   def render(assigns) do
     ~H"""
-    <.live_table
-      id="oban_jobs"
-      dom_id="oban-jobs"
-      page={@page}
-      row_attrs={&row_attrs/1}
-      row_fetcher={&fetch_jobs/2}
-      title="Oban Jobs"
-      search={false}
-    >
-      <:col field={:id} header="ID" sortable={:desc} />
-      <:col field={:state} sortable={:desc} />
-      <:col field={:queue} sortable={:desc} />
-      <:col field={:worker} sortable={:desc} />
-      <:col :let={job} field={:attempt} header="Attempts" sortable={:desc}>
-        <%= job.attempt %>/<%= job.max_attempts %>
-      
-      <:col :let={job} field={:inserted_at} sortable={:desc}>
-        <%= format_value(job.inserted_at) %>
-      
-      <:col :let={job} field={:scheduled_at} sortable={:desc}>
-        <%= format_value(job.scheduled_at) %>
-      
-    
-    <.live_modal
-      :if={@job != nil}
-      id="modal"
-      title="Job"
-      return_to={live_dashboard_path(@socket, @page, params: %{})}
-    >
+    

Oban

+ +

Filter jobs by state:

+ + <.live_nav_bar id="oban_states" page={@page} nav_param="job_state" style={:bar} extra_params={["nav"]}> + <:item :for={{job_state, count} <- @job_state_counts} name={job_state} label={job_state_label(job_state, count)} method="navigate"> + <.live_table id="oban_jobs" limit={per_page_limits()} dom_id={"oban-jobs-#{job_state}"} page={@page} row_attrs={&row_attrs/1} row_fetcher={&fetch_jobs(&1, &2, job_state)} default_sort_by={@timestamp_field} title="" search={false}> + <:col :let={job} field={:worker} sortable={:desc}> +

<%= job.worker %>

+
<%= truncate(inspect(job.args)) %>
+ + <:col :let={job} field={:attempt} header="Attempt" sortable={:desc}> + + <%= job.attempt %>/<%= job.max_attempts %> + + + <:col field={:queue} header="Queue" sortable={:desc} /> + <:col :let={job} field={@timestamp_field} sortable={:desc}> + <%= Timex.from_now(timestamp(job)) %> + + + + + + <.live_modal :if={@job != nil} id="job-modal" title={"Job - #{@job.id}"} return_to={live_dashboard_path(@socket, @page, params: %{})}> <.label_value_list> <:elem label="ID"><%= @job.id %> <:elem label="State"><%= @job.state %> @@ -53,15 +56,9 @@ defmodule Oban.LiveDashboard do <:elem label="Attempts"><%= @job.attempt %>/<%= @job.max_attempts %> <:elem label="Priority"><%= @job.priority %> <:elem label="Attempted at"><%= format_value(@job.attempted_at) %> - <:elem :if={@job.cancelled_at} label="Cancelled at"> - <%= format_value(@job.cancelled_at) %> - - <:elem :if={@job.completed_at} label="Completed at"> - <%= format_value(@job.completed_at) %> - - <:elem :if={@job.discarded_at} label="Discarded at"> - <%= format_value(@job.discarded_at) %> - + <:elem :if={@job.cancelled_at} label="Cancelled at"><%= format_value(@job.cancelled_at) %> + <:elem :if={@job.completed_at} label="Completed at"><%= format_value(@job.completed_at) %> + <:elem :if={@job.discarded_at} label="Discarded at"><%= format_value(@job.discarded_at) %> <:elem label="Inserted at"><%= format_value(@job.inserted_at) %> <:elem label="Scheduled at"><%= format_value(@job.scheduled_at) %> @@ -69,8 +66,29 @@ defmodule Oban.LiveDashboard do """ end + @impl true + def mount(params, _, socket) do + socket = + socket + |> assign(job_state: Map.get(params, "job_state", "executing")) + |> assign(sort_by: Map.get(params, "job_state")) + + {:ok, socket} + end + + @impl true + def menu_link(_, _) do + {:ok, "Oban"} + end + @impl true def handle_params(%{"params" => %{"job" => job_id}}, _url, socket) do + socket = + socket + |> assign(job: nil) + |> assign_job_state_counts() + |> assign_timestamp_field() + case fetch_job(job_id) do {:ok, job} -> {:noreply, assign(socket, job: job)} @@ -81,8 +99,14 @@ defmodule Oban.LiveDashboard do end end - def handle_params(_params, _url, socket) do - {:noreply, assign(socket, job: nil)} + def handle_params(_params, _uri, socket) do + socket = + socket + |> assign(job: nil) + |> assign_job_state_counts() + |> assign_timestamp_field() + + {:noreply, socket} end @impl true @@ -91,28 +115,68 @@ defmodule Oban.LiveDashboard do {:noreply, push_patch(socket, to: to)} end - defp fetch_jobs(params, _node) do - total_jobs = Oban.Repo.aggregate(Oban.config(), Oban.Job, :count) - jobs = Oban.Repo.all(Oban.config(), jobs_query(params)) |> Enum.map(&Map.from_struct/1) + @impl true + def handle_refresh(socket) do + socket = + socket + |> assign_job_state_counts() + + {:noreply, socket} + end + + defp assign_job_state_counts(socket) do + job_state_counts_in_db = + Oban.Repo.all( + Oban.config(), + Oban.Job + |> group_by([j], [j.state]) + |> order_by([j], [j.state]) + |> select([j], {j.state, count(j.id)}) + ) + |> Enum.into(%{}) + + job_state_counts = + for job_state <- @oban_sorted_job_states, + do: {job_state, Map.get(job_state_counts_in_db, job_state, 0)} + + assign(socket, job_state_counts: job_state_counts) + end + + defp job_state_label(job_state, count) do + "#{job_state} - (#{count})" + end + + defp fetch_jobs(params, _node, job_state) do + total_jobs = Oban.Repo.aggregate(Oban.config(), jobs_count_query(job_state), :count) + + jobs = + Oban.Repo.all(Oban.config(), jobs_query(params, job_state)) |> Enum.map(&Map.from_struct/1) + {jobs, total_jobs} end defp fetch_job(id) do case Oban.Repo.get(Oban.config(), Oban.Job, id) do - nil -> - :error - - job -> + %Oban.Job{} = job -> {:ok, job} + + _ -> + :error end end - defp jobs_query(%{sort_by: sort_by, sort_dir: sort_dir, limit: l}) do + defp jobs_query(%{sort_by: sort_by, sort_dir: sort_dir, limit: limit}, job_state) do Oban.Job - |> limit(^l) + |> limit(^limit) + |> where([job], job.state == ^job_state) |> order_by({^sort_dir, ^sort_by}) end + defp jobs_count_query(job_state) do + Oban.Job + |> where([job], job.state == ^job_state) + end + defp row_attrs(job) do [ {"phx-click", "show_job"}, @@ -125,9 +189,47 @@ defmodule Oban.LiveDashboard do Enum.map(errors, &Map.get(&1, "error")) end - def format_value(%DateTime{} = datetime) do + defp format_value(%DateTime{} = datetime) do DateTime.to_string(datetime) end - def format_value(nil), do: nil + defp format_value(nil), do: nil + + defp timestamp(job) do + case job.state do + "available" -> job.scheduled_at + "cancelled" -> job.cancelled_at + "completed" -> job.completed_at + "discarded" -> job.discarded_at + "executing" -> job.attempted_at + "retryable" -> job.scheduled_at + "scheduled" -> job.scheduled_at + end + end + + defp assign_timestamp_field(%{assigns: %{job_state: job_state}} = socket) do + timestamp_field = + case job_state do + "available" -> :scheduled_at + "cancelled" -> :cancelled_at + "completed" -> :completed_at + "discarded" -> :discarded_at + "executing" -> :attempted_at + "retryable" -> :scheduled_at + "scheduled" -> :scheduled_at + _ -> :attempted_at + end + + assign(socket, timestamp_field: timestamp_field) + end + + defp truncate(string, max_length \\ 50) do + if String.length(string) > max_length do + String.slice(string, 0, max_length) <> "…" + else + string + end + end + + defp per_page_limits, do: @per_page_limits end From 4b5a99f41069c0c1c790b42cd6fdfdd80196f851 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sat, 9 Nov 2024 16:31:25 +0100 Subject: [PATCH 02/13] add timex, fix tests --- mix.exs | 1 + mix.lock | 13 +++++++++++++ test/oban/live_dashboard_test.exs | 19 +++++++++++++++---- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index c3a8726..d171277 100644 --- a/mix.exs +++ b/mix.exs @@ -26,6 +26,7 @@ defmodule Oban.LiveDashboard.MixProject do {:phoenix_live_dashboard, "~> 0.7"}, {:floki, ">= 0.30.0", only: :test}, {:ecto_sqlite3, ">= 0.0.0", only: :test}, + {:timex, "~> 3.7"}, {:oban, "~> 2.15"} ] end diff --git a/mix.lock b/mix.lock index eaf98d8..7e4950b 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,8 @@ %{ "castore": {:hex, :castore, "1.0.9", "5cc77474afadf02c7c017823f460a17daa7908e991b0cc917febc90e466a375c", [:mix], [], "hexpm", "5ea956504f1ba6f2b4eb707061d8e17870de2bee95fb59d512872c2ef06925e7"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"}, + "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, + "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "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.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, @@ -9,15 +11,22 @@ "ecto_sqlite3": {:hex, :ecto_sqlite3, "0.17.4", "48dd9c6d0fc10875a64545d04f0478b142898b6f0e73ae969becf5726f834d22", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.12", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.22", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "f67372e0eae5e5cbdd1d145e78e670fc5064d5810adf99d104d364cb920e306a"}, "elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, + "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, "exqlite": {:hex, :exqlite, "0.27.0", "2ef6021862e74c6253d1fb1f5701bd47e4e779b035d34daf2a13ec83945a05ba", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "b947b9db15bb7aad11da6cd18a0d8b78f7fcce89508a27a5b9be18350fe12c59"}, "floki": {:hex, :floki, "0.36.3", "1102f93b16a55bc5383b85ae3ec470f82dee056eaeff9195e8afdf0ef2a43c30", [:mix], [], "hexpm", "fe0158bff509e407735f6d40b3ee0d7deb47f3f3ee7c6c182ad28599f9f6b27a"}, + "gettext": {:hex, :gettext, "0.26.1", "38e14ea5dcf962d1fc9f361b63ea07c0ce715a8ef1f9e82d3dfb8e67e0416715", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "01ce56f188b9dc28780a52783d6529ad2bc7124f9744e571e1ee4ea88bf08734"}, + "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, + "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [: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", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"}, "phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.4", "4508e481f791ce62ec6a096e13b061387158cbeefacca68c6c1928e1305e23ed", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "2984aae96994fbc5c61795a73b8fb58153b41ff934019cfb522343d2d3817d59"}, @@ -26,8 +35,12 @@ "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"}, "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"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "telemetry_metrics": {:hex, :telemetry_metrics, "1.0.0", "29f5f84991ca98b8eb02fc208b2e6de7c95f8bb2294ef244a176675adc7775df", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f23713b3847286a534e005126d4c959ebcca68ae9582118ce436b521d1d47d5d"}, + "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, + "tzdata": {:hex, :tzdata, "1.1.2", "45e5f1fcf8729525ec27c65e163be5b3d247ab1702581a94674e008413eef50b", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cec7b286e608371602318c414f344941d5eb0375e14cfdab605cca2fe66cba8b"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [: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", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"}, } diff --git a/test/oban/live_dashboard_test.exs b/test/oban/live_dashboard_test.exs index e87f878..57a20c3 100644 --- a/test/oban/live_dashboard_test.exs +++ b/test/oban/live_dashboard_test.exs @@ -14,15 +14,19 @@ defmodule Oban.LiveDashboardTest do test "shows jobs with limit" do for _ <- 1..110, do: job_fixture() {:ok, live, rendered} = live(build_conn(), "/dashboard/oban") - assert rendered |> :binary.matches(" length() <= 100 + + assert rendered |> :binary.matches(" length() == + 20 rendered = render_patch(live, "/dashboard/oban?limit=100") - assert rendered |> :binary.matches(" length() == 100 + + assert rendered |> :binary.matches(" length() == + 100 end test "shows job info modal" do job = job_fixture(%{something: "foobar"}) - {:ok, live, rendered} = live(build_conn(), "/dashboard/oban?params[job]=#{job.id}") + {:ok, live, _rendered} = live(build_conn(), "/dashboard/oban?params[job]=#{job.id}") rendered = render(live) assert rendered =~ "modal-content" assert rendered =~ "%{"something" => "foobar"}" @@ -30,7 +34,14 @@ defmodule Oban.LiveDashboardTest do end defp job_fixture(args \\ %{}) do - {:ok, job} = Oban.Job.new(args, worker: "FakeWorker") |> Oban.insert() + {:ok, job} = + Oban.Job.new(args, + worker: "FakeWorker", + state: "executing", + attempted_at: DateTime.utc_now() + ) + |> Oban.insert() + job end end From 95ab7b10ef1435e36644cec0f16ad77d83b1a1cc Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sun, 10 Nov 2024 10:25:42 +0100 Subject: [PATCH 03/13] bring back all tab --- lib/oban/live_dashboard.ex | 37 +++++++++++++++++-------------- test/oban/live_dashboard_test.exs | 4 ++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex index 88b3723..925e0ed 100644 --- a/lib/oban/live_dashboard.ex +++ b/lib/oban/live_dashboard.ex @@ -27,17 +27,15 @@ defmodule Oban.LiveDashboard do <:item :for={{job_state, count} <- @job_state_counts} name={job_state} label={job_state_label(job_state, count)} method="navigate"> <.live_table id="oban_jobs" limit={per_page_limits()} dom_id={"oban-jobs-#{job_state}"} page={@page} row_attrs={&row_attrs/1} row_fetcher={&fetch_jobs(&1, &2, job_state)} default_sort_by={@timestamp_field} title="" search={false}> <:col :let={job} field={:worker} sortable={:desc}> -

<%= job.worker %>

-
<%= truncate(inspect(job.args)) %>
+

<%= job.worker %>

+
<%= truncate(inspect(job.args)) %>
<:col :let={job} field={:attempt} header="Attempt" sortable={:desc}> - - <%= job.attempt %>/<%= job.max_attempts %> - + <%= job.attempt %>/<%= job.max_attempts %> <:col field={:queue} header="Queue" sortable={:desc} /> <:col :let={job} field={@timestamp_field} sortable={:desc}> - <%= Timex.from_now(timestamp(job)) %> + <%= Timex.from_now(timestamp(job, @timestamp_field)) %> @@ -139,6 +137,9 @@ defmodule Oban.LiveDashboard do for job_state <- @oban_sorted_job_states, do: {job_state, Map.get(job_state_counts_in_db, job_state, 0)} + total_count = Keyword.values(job_state_counts) |> Enum.sum() + job_state_counts = [{"all", total_count} | job_state_counts] + assign(socket, job_state_counts: job_state_counts) end @@ -165,6 +166,12 @@ defmodule Oban.LiveDashboard do end end + defp jobs_query(%{sort_by: sort_by, sort_dir: sort_dir, limit: limit}, "all" = _job_state) do + Oban.Job + |> limit(^limit) + |> order_by({^sort_dir, ^sort_by}) + end + defp jobs_query(%{sort_by: sort_by, sort_dir: sort_dir, limit: limit}, job_state) do Oban.Job |> limit(^limit) @@ -172,6 +179,10 @@ defmodule Oban.LiveDashboard do |> order_by({^sort_dir, ^sort_by}) end + defp jobs_count_query("all" = _job_state) do + Oban.Job + end + defp jobs_count_query(job_state) do Oban.Job |> where([job], job.state == ^job_state) @@ -195,16 +206,8 @@ defmodule Oban.LiveDashboard do defp format_value(nil), do: nil - defp timestamp(job) do - case job.state do - "available" -> job.scheduled_at - "cancelled" -> job.cancelled_at - "completed" -> job.completed_at - "discarded" -> job.discarded_at - "executing" -> job.attempted_at - "retryable" -> job.scheduled_at - "scheduled" -> job.scheduled_at - end + defp timestamp(job, timestamp_field) do + Map.get(job, timestamp_field) end defp assign_timestamp_field(%{assigns: %{job_state: job_state}} = socket) do @@ -217,7 +220,7 @@ defmodule Oban.LiveDashboard do "executing" -> :attempted_at "retryable" -> :scheduled_at "scheduled" -> :scheduled_at - _ -> :attempted_at + _ -> :inserted_at end assign(socket, timestamp_field: timestamp_field) diff --git a/test/oban/live_dashboard_test.exs b/test/oban/live_dashboard_test.exs index 57a20c3..07d8bad 100644 --- a/test/oban/live_dashboard_test.exs +++ b/test/oban/live_dashboard_test.exs @@ -15,12 +15,12 @@ defmodule Oban.LiveDashboardTest do for _ <- 1..110, do: job_fixture() {:ok, live, rendered} = live(build_conn(), "/dashboard/oban") - assert rendered |> :binary.matches(" length() == + assert rendered |> :binary.matches(" length() == 20 rendered = render_patch(live, "/dashboard/oban?limit=100") - assert rendered |> :binary.matches(" length() == + assert rendered |> :binary.matches(" length() == 100 end From 75afa7b53f2280c977949c60e1fb81097e248179 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sun, 10 Nov 2024 12:52:02 +0100 Subject: [PATCH 04/13] remove Timex --- lib/oban/live_dashboard.ex | 6 +++--- mix.exs | 1 - mix.lock | 23 +++++------------------ 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex index 925e0ed..28cc051 100644 --- a/lib/oban/live_dashboard.ex +++ b/lib/oban/live_dashboard.ex @@ -35,7 +35,7 @@ defmodule Oban.LiveDashboard do <:col field={:queue} header="Queue" sortable={:desc} /> <:col :let={job} field={@timestamp_field} sortable={:desc}> - <%= Timex.from_now(timestamp(job, @timestamp_field)) %> + <%= format_value(timestamp(job, @timestamp_field)) %> @@ -201,10 +201,10 @@ defmodule Oban.LiveDashboard do end defp format_value(%DateTime{} = datetime) do - DateTime.to_string(datetime) + Calendar.strftime(datetime, "%Y-%m-%d %H:%M:%S") end - defp format_value(nil), do: nil + defp format_value(value), do: value defp timestamp(job, timestamp_field) do Map.get(job, timestamp_field) diff --git a/mix.exs b/mix.exs index d171277..c3a8726 100644 --- a/mix.exs +++ b/mix.exs @@ -26,7 +26,6 @@ defmodule Oban.LiveDashboard.MixProject do {:phoenix_live_dashboard, "~> 0.7"}, {:floki, ">= 0.30.0", only: :test}, {:ecto_sqlite3, ">= 0.0.0", only: :test}, - {:timex, "~> 3.7"}, {:oban, "~> 2.15"} ] end diff --git a/mix.lock b/mix.lock index 7e4950b..27e2e6b 100644 --- a/mix.lock +++ b/mix.lock @@ -1,8 +1,6 @@ %{ "castore": {:hex, :castore, "1.0.9", "5cc77474afadf02c7c017823f460a17daa7908e991b0cc917febc90e466a375c", [:mix], [], "hexpm", "5ea956504f1ba6f2b4eb707061d8e17870de2bee95fb59d512872c2ef06925e7"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"}, - "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, - "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "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.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, @@ -11,22 +9,15 @@ "ecto_sqlite3": {:hex, :ecto_sqlite3, "0.17.4", "48dd9c6d0fc10875a64545d04f0478b142898b6f0e73ae969becf5726f834d22", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.12", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:exqlite, "~> 0.22", [hex: :exqlite, repo: "hexpm", optional: false]}], "hexpm", "f67372e0eae5e5cbdd1d145e78e670fc5064d5810adf99d104d364cb920e306a"}, "elixir_make": {:hex, :elixir_make, "0.8.4", "4960a03ce79081dee8fe119d80ad372c4e7badb84c493cc75983f9d3bc8bde0f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "6e7f1d619b5f61dfabd0a20aa268e575572b542ac31723293a4c1a567d5ef040"}, "ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"}, - "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, "exqlite": {:hex, :exqlite, "0.27.0", "2ef6021862e74c6253d1fb1f5701bd47e4e779b035d34daf2a13ec83945a05ba", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "b947b9db15bb7aad11da6cd18a0d8b78f7fcce89508a27a5b9be18350fe12c59"}, "floki": {:hex, :floki, "0.36.3", "1102f93b16a55bc5383b85ae3ec470f82dee056eaeff9195e8afdf0ef2a43c30", [:mix], [], "hexpm", "fe0158bff509e407735f6d40b3ee0d7deb47f3f3ee7c6c182ad28599f9f6b27a"}, - "gettext": {:hex, :gettext, "0.26.1", "38e14ea5dcf962d1fc9f361b63ea07c0ce715a8ef1f9e82d3dfb8e67e0416715", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "01ce56f188b9dc28780a52783d6529ad2bc7124f9744e571e1ee4ea88bf08734"}, - "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, - "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, - "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, - "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, + "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.0", "74bb8348c9b3a51d5c589bf5aebb0466a84b33274150e3b6ece1da45584afc82", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49159b7d7d999e836bedaf09dcf35ca18b312230cf901b725a64f3f42e407983"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, + "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"}, - "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [: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", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"}, "phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.4", "4508e481f791ce62ec6a096e13b061387158cbeefacca68c6c1928e1305e23ed", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "2984aae96994fbc5c61795a73b8fb58153b41ff934019cfb522343d2d3817d59"}, @@ -35,12 +26,8 @@ "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"}, "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"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "telemetry_metrics": {:hex, :telemetry_metrics, "1.0.0", "29f5f84991ca98b8eb02fc208b2e6de7c95f8bb2294ef244a176675adc7775df", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f23713b3847286a534e005126d4c959ebcca68ae9582118ce436b521d1d47d5d"}, - "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, - "tzdata": {:hex, :tzdata, "1.1.2", "45e5f1fcf8729525ec27c65e163be5b3d247ab1702581a94674e008413eef50b", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cec7b286e608371602318c414f344941d5eb0375e14cfdab605cca2fe66cba8b"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, - "websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [: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", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [: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", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"}, } From 30545440370a471c9504bd5e9bc615c99d180a55 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sun, 10 Nov 2024 23:51:19 +0100 Subject: [PATCH 05/13] Update lib/oban/live_dashboard.ex Co-authored-by: Marty Zalega --- lib/oban/live_dashboard.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex index 28cc051..16f1860 100644 --- a/lib/oban/live_dashboard.ex +++ b/lib/oban/live_dashboard.ex @@ -27,8 +27,8 @@ defmodule Oban.LiveDashboard do <:item :for={{job_state, count} <- @job_state_counts} name={job_state} label={job_state_label(job_state, count)} method="navigate"> <.live_table id="oban_jobs" limit={per_page_limits()} dom_id={"oban-jobs-#{job_state}"} page={@page} row_attrs={&row_attrs/1} row_fetcher={&fetch_jobs(&1, &2, job_state)} default_sort_by={@timestamp_field} title="" search={false}> <:col :let={job} field={:worker} sortable={:desc}> -

<%= job.worker %>

-
<%= truncate(inspect(job.args)) %>
+

<%= job.worker %>

+
<%= truncate(inspect(job.args)) %>
<:col :let={job} field={:attempt} header="Attempt" sortable={:desc}> <%= job.attempt %>/<%= job.max_attempts %> From 8fb6beca36b7dc5ed002ea27f677d79b5fb5ceae Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sun, 10 Nov 2024 23:52:34 +0100 Subject: [PATCH 06/13] Update lib/oban/live_dashboard.ex Co-authored-by: Marty Zalega --- lib/oban/live_dashboard.ex | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex index 16f1860..44c4925 100644 --- a/lib/oban/live_dashboard.ex +++ b/lib/oban/live_dashboard.ex @@ -172,20 +172,28 @@ defmodule Oban.LiveDashboard do |> order_by({^sort_dir, ^sort_by}) end - defp jobs_query(%{sort_by: sort_by, sort_dir: sort_dir, limit: limit}, job_state) do + defp jobs_query(params, job_state) do Oban.Job - |> limit(^limit) - |> where([job], job.state == ^job_state) - |> order_by({^sort_dir, ^sort_by}) + |> filter_by_job_state(job_state) + |> filter_by_params(params) end - - defp jobs_count_query("all" = _job_state) do + + defp jobs_count_query("all") do Oban.Job end defp jobs_count_query(job_state) do - Oban.Job - |> where([job], job.state == ^job_state) + filter_by_job_state(Oban.Job, job_state) + end + + defp filter_by_params(queryable, %{sort_by: sort_by, sort_dir: sort_dir, limit: limit}) do + queryable + |> limit(^limit) + |> order_by({^sort_dir, ^sort_by}) + end + + defp filter_by_job_state(queryable, job_state) do + where(queryable, [job], job.state == ^job_state) end defp row_attrs(job) do From 14b2dff9990edde13d8cab00f8a6fd3335b1609c Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sun, 10 Nov 2024 23:54:14 +0100 Subject: [PATCH 07/13] Update lib/oban/live_dashboard.ex Co-authored-by: Marty Zalega --- lib/oban/live_dashboard.ex | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex index 44c4925..068a345 100644 --- a/lib/oban/live_dashboard.ex +++ b/lib/oban/live_dashboard.ex @@ -19,10 +19,7 @@ defmodule Oban.LiveDashboard do @impl true def render(assigns) do ~H""" -

Oban

- -

Filter jobs by state:

- +
Oban
<.live_nav_bar id="oban_states" page={@page} nav_param="job_state" style={:bar} extra_params={["nav"]}> <:item :for={{job_state, count} <- @job_state_counts} name={job_state} label={job_state_label(job_state, count)} method="navigate"> <.live_table id="oban_jobs" limit={per_page_limits()} dom_id={"oban-jobs-#{job_state}"} page={@page} row_attrs={&row_attrs/1} row_fetcher={&fetch_jobs(&1, &2, job_state)} default_sort_by={@timestamp_field} title="" search={false}> From a0432ad87cffecffdbf07631dd84fca046987121 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Sun, 10 Nov 2024 23:56:14 +0100 Subject: [PATCH 08/13] revert mix.lock --- mix.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mix.lock b/mix.lock index 27e2e6b..eaf98d8 100644 --- a/mix.lock +++ b/mix.lock @@ -12,10 +12,10 @@ "exqlite": {:hex, :exqlite, "0.27.0", "2ef6021862e74c6253d1fb1f5701bd47e4e779b035d34daf2a13ec83945a05ba", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "b947b9db15bb7aad11da6cd18a0d8b78f7fcce89508a27a5b9be18350fe12c59"}, "floki": {:hex, :floki, "0.36.3", "1102f93b16a55bc5383b85ae3ec470f82dee056eaeff9195e8afdf0ef2a43c30", [:mix], [], "hexpm", "fe0158bff509e407735f6d40b3ee0d7deb47f3f3ee7c6c182ad28599f9f6b27a"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, - "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, - "makeup_elixir": {:hex, :makeup_elixir, "1.0.0", "74bb8348c9b3a51d5c589bf5aebb0466a84b33274150e3b6ece1da45584afc82", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49159b7d7d999e836bedaf09dcf35ca18b312230cf901b725a64f3f42e407983"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, - "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, + "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"}, "phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [: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", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"}, @@ -29,5 +29,5 @@ "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "telemetry_metrics": {:hex, :telemetry_metrics, "1.0.0", "29f5f84991ca98b8eb02fc208b2e6de7c95f8bb2294ef244a176675adc7775df", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f23713b3847286a534e005126d4c959ebcca68ae9582118ce436b521d1d47d5d"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, - "websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [: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", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [: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", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"}, } From a68eec2ab232145390e54f04776b4e77d5d4f403 Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Mon, 11 Nov 2024 00:34:54 +0100 Subject: [PATCH 09/13] add test for switching states --- lib/oban/live_dashboard.ex | 6 ++--- test/oban/live_dashboard_test.exs | 45 +++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex index 068a345..b2bc917 100644 --- a/lib/oban/live_dashboard.ex +++ b/lib/oban/live_dashboard.ex @@ -174,7 +174,7 @@ defmodule Oban.LiveDashboard do |> filter_by_job_state(job_state) |> filter_by_params(params) end - + defp jobs_count_query("all") do Oban.Job end @@ -182,13 +182,13 @@ defmodule Oban.LiveDashboard do defp jobs_count_query(job_state) do filter_by_job_state(Oban.Job, job_state) end - + defp filter_by_params(queryable, %{sort_by: sort_by, sort_dir: sort_dir, limit: limit}) do queryable |> limit(^limit) |> order_by({^sort_dir, ^sort_by}) end - + defp filter_by_job_state(queryable, job_state) do where(queryable, [job], job.state == ^job_state) end diff --git a/test/oban/live_dashboard_test.exs b/test/oban/live_dashboard_test.exs index 07d8bad..60462d8 100644 --- a/test/oban/live_dashboard_test.exs +++ b/test/oban/live_dashboard_test.exs @@ -24,6 +24,47 @@ defmodule Oban.LiveDashboardTest do 100 end + test "switch between states" do + Oban.LiveDashboardTest.Repo.delete_all(Oban.Job) + _executing_job = job_fixture(%{"foo" => "executing"}, "executing") + _completed_job = job_fixture(%{"foo" => "completed"}, "completed") + + conn = build_conn() + {:ok, live, rendered} = live(conn, "/dashboard/oban") + + assert rendered |> :binary.matches(" length() == 2 + + {:ok, live, rendered} = + live + |> element("a", "executing - (1)") + |> render_click() + |> follow_redirect(conn) + + assert rendered + |> :binary.matches(" length() == 1 + + {:ok, live, rendered} = + live + |> element("a", "completed - (1)") + |> render_click() + |> follow_redirect(conn) + + assert rendered + |> :binary.matches(" length() == 1 + + {:ok, _live, rendered} = + live + |> element("a", "scheduled - (0)") + |> render_click() + |> follow_redirect(conn) + + assert rendered + |> :binary.matches(" length() == 0 + end + test "shows job info modal" do job = job_fixture(%{something: "foobar"}) {:ok, live, _rendered} = live(build_conn(), "/dashboard/oban?params[job]=#{job.id}") @@ -33,11 +74,11 @@ defmodule Oban.LiveDashboardTest do refute live |> element("#modal-close") |> render_click() =~ "modal" end - defp job_fixture(args \\ %{}) do + defp job_fixture(args \\ %{}, state \\ "executing") do {:ok, job} = Oban.Job.new(args, worker: "FakeWorker", - state: "executing", + state: state, attempted_at: DateTime.utc_now() ) |> Oban.insert() From aeda38df7de8da54877b27c683a86aa7cd0781cc Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Mon, 11 Nov 2024 00:37:46 +0100 Subject: [PATCH 10/13] typo --- test/oban/live_dashboard_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/oban/live_dashboard_test.exs b/test/oban/live_dashboard_test.exs index 60462d8..ffc9d22 100644 --- a/test/oban/live_dashboard_test.exs +++ b/test/oban/live_dashboard_test.exs @@ -61,7 +61,7 @@ defmodule Oban.LiveDashboardTest do |> follow_redirect(conn) assert rendered - |> :binary.matches(" :binary.matches(" length() == 0 end From d889822714555996683aeb20eda21aa48ae2129b Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Mon, 11 Nov 2024 10:42:29 +0100 Subject: [PATCH 11/13] Apply suggestions from code review Co-authored-by: Marty Zalega --- lib/oban/live_dashboard.ex | 10 +++------- test/oban/live_dashboard_test.exs | 11 +++-------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex index b2bc917..393cff8 100644 --- a/lib/oban/live_dashboard.ex +++ b/lib/oban/live_dashboard.ex @@ -112,11 +112,7 @@ defmodule Oban.LiveDashboard do @impl true def handle_refresh(socket) do - socket = - socket - |> assign_job_state_counts() - - {:noreply, socket} + {:noreply, assign_job_state_counts(socket)} end defp assign_job_state_counts(socket) do @@ -141,7 +137,7 @@ defmodule Oban.LiveDashboard do end defp job_state_label(job_state, count) do - "#{job_state} - (#{count})" + "#{Phoenix.Naming.humanize(job_state)} (#{count})" end defp fetch_jobs(params, _node, job_state) do @@ -163,7 +159,7 @@ defmodule Oban.LiveDashboard do end end - defp jobs_query(%{sort_by: sort_by, sort_dir: sort_dir, limit: limit}, "all" = _job_state) do + defp jobs_query(%{sort_by: sort_by, sort_dir: sort_dir, limit: limit}, "all") do Oban.Job |> limit(^limit) |> order_by({^sort_dir, ^sort_by}) diff --git a/test/oban/live_dashboard_test.exs b/test/oban/live_dashboard_test.exs index ffc9d22..6d63c12 100644 --- a/test/oban/live_dashboard_test.exs +++ b/test/oban/live_dashboard_test.exs @@ -74,14 +74,9 @@ defmodule Oban.LiveDashboardTest do refute live |> element("#modal-close") |> render_click() =~ "modal" end - defp job_fixture(args \\ %{}, state \\ "executing") do - {:ok, job} = - Oban.Job.new(args, - worker: "FakeWorker", - state: state, - attempted_at: DateTime.utc_now() - ) - |> Oban.insert() + defp job_fixture(args \\ %{}, opts \\ []) do + opts = Keyword.put(opts, :worker, "FakeWorker") + {:ok, job} = Oban.Job.new(args, opts) |> Oban.insert() job end From c1e4f9fa150262f88ab6f68e8fe406f407c33a9d Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Mon, 11 Nov 2024 21:17:52 +0100 Subject: [PATCH 12/13] fix compile warning --- lib/oban/live_dashboard.ex | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex index c495895..575ea3b 100644 --- a/lib/oban/live_dashboard.ex +++ b/lib/oban/live_dashboard.ex @@ -147,11 +147,6 @@ defmodule Oban.LiveDashboard do Oban.Repo.get(Oban.config(), Oban.Job, id) end - @impl true - def handle_refresh(socket) do - {:noreply, assign_job_state_counts(socket)} - end - @impl true def handle_refresh(socket) do {:noreply, From f1f23d809c73141db35e50bc230e060247b38ede Mon Sep 17 00:00:00 2001 From: Aleksandr Lossenko Date: Mon, 11 Nov 2024 22:04:45 +0100 Subject: [PATCH 13/13] handle_params --- lib/oban/live_dashboard.ex | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/oban/live_dashboard.ex b/lib/oban/live_dashboard.ex index 575ea3b..b295cf6 100644 --- a/lib/oban/live_dashboard.ex +++ b/lib/oban/live_dashboard.ex @@ -72,12 +72,7 @@ defmodule Oban.LiveDashboard do end @impl true - def mount(params, _, socket) do - socket = - socket - |> assign(job_state: Map.get(params, "job_state", "executing")) - |> assign(sort_by: Map.get(params, "job_state")) - + def mount(_params, _, socket) do {:ok, socket} end @@ -87,9 +82,11 @@ defmodule Oban.LiveDashboard do end @impl true - def handle_params(%{"params" => %{"job" => job_id}}, _url, socket) do + def handle_params(%{"params" => %{"job" => job_id}} = params, _url, socket) do socket = socket + |> assign(job_state: Map.get(params, "job_state", "executing")) + |> assign(sort_by: Map.get(params, "job_state")) |> assign(job: nil) |> assign_job_state_counts() |> assign_timestamp_field() @@ -104,9 +101,11 @@ defmodule Oban.LiveDashboard do end end - def handle_params(_params, _uri, socket) do + def handle_params(params, _uri, socket) do socket = socket + |> assign(job_state: Map.get(params, "job_state", "executing")) + |> assign(sort_by: Map.get(params, "job_state")) |> assign(job: nil) |> assign_job_state_counts() |> assign_timestamp_field()