From 71d978a355e3863e42b31bd09aaaedc9419d1176 Mon Sep 17 00:00:00 2001 From: Dejan K Date: Tue, 21 Oct 2025 14:09:40 +0200 Subject: [PATCH 1/4] feat(front): add listing mode to workflow queries and UI --- front/assets/js/memory_cookie.js | 1 + front/assets/js/workflow_list.js | 2 +- front/lib/front/memory_cookie.ex | 1 + front/lib/front/project_page/model.ex | 67 ++++++++++++++++--- .../controllers/project_controller.ex | 37 +++++++++- .../templates/project/_tabs.html.eex | 7 ++ .../templates/project/private.html.eex | 9 ++- .../templates/project/public.html.eex | 8 ++- .../templates/project/public/_tabs.html.eex | 7 ++ front/test/front/project_page/model_test.exs | 2 +- 10 files changed, 126 insertions(+), 15 deletions(-) diff --git a/front/assets/js/memory_cookie.js b/front/assets/js/memory_cookie.js index 7f0a572de..bdae73bc0 100644 --- a/front/assets/js/memory_cookie.js +++ b/front/assets/js/memory_cookie.js @@ -31,6 +31,7 @@ export class MemoryCookie { rootSidebar: false, rootRequester: true, projectType: "", + projectListing: "latest", projectRequester: false, logDark: false, logWrap: true, diff --git a/front/assets/js/workflow_list.js b/front/assets/js/workflow_list.js index cc0ae5135..98ac85e6f 100644 --- a/front/assets/js/workflow_list.js +++ b/front/assets/js/workflow_list.js @@ -7,7 +7,7 @@ export var WorkflowList = { pagination: null, pollmanList: null, container: null, - queryParams: ['page_token', 'direction', 'date_from', 'date_to', 'author'], + queryParams: ['page_token', 'direction', 'date_from', 'date_to', 'author', 'listing'], init: function() { if(this.initiated === true) { return; } diff --git a/front/lib/front/memory_cookie.ex b/front/lib/front/memory_cookie.ex index 5c377bc39..1d366c0fa 100644 --- a/front/lib/front/memory_cookie.ex +++ b/front/lib/front/memory_cookie.ex @@ -18,6 +18,7 @@ defmodule Front.MemoryCookie do "rootSidebar" => false, "rootRequester" => true, "projectType" => "", + "projectListing" => "latest", "projectRequester" => "false", "logDark" => false, "logWrap" => true, diff --git a/front/lib/front/project_page/model.ex b/front/lib/front/project_page/model.ex index bad986dcb..a8bad0c3c 100644 --- a/front/lib/front/project_page/model.ex +++ b/front/lib/front/project_page/model.ex @@ -1,12 +1,14 @@ defmodule Front.ProjectPage.Model do use TypedStruct alias Front.{Async, Decorators, Models} + alias InternalApi.PlumberWF.ListKeysetRequest.Direction, as: KeysetDirection require Logger typedstruct do field(:project, Front.Model.Project.t()) field(:page_token, String.t()) field(:direction, String.t()) + field(:list_mode, String.t()) field(:user_page?, boolean()) field(:ref_types, String.t()) field(:workflows, [Front.Model.Workflow.t()], enforce: true) @@ -35,6 +37,7 @@ defmodule Front.ProjectPage.Model do field(:user_id, String.t()) field(:page_token, String.t(), default: "") field(:direction, String.t()) + field(:list_mode, String.t(), default: "latest") field(:user_page?, boolean()) field(:ref_types, [String.t()]) end @@ -51,7 +54,8 @@ defmodule Front.ProjectPage.Model do @spec get(LoadParams.t()) :: {:ok, __MODULE__.t()} | {:error, String.t()} def get(params, opts \\ []) do with true <- first_page?(params), - true <- everyones_page?(params) do + true <- everyones_page?(params), + true <- cacheable_mode?(params) do fetch_from_cache(params, opts[:force_cold_boot]) else false -> @@ -79,7 +83,7 @@ defmodule Front.ProjectPage.Model do end def cache_key(params) do - "#{cache_prefix()}/#{cache_version()}/project_id=#{params.project_id}/ref_types=#{params.ref_types}/" + "#{cache_prefix()}/#{cache_version()}/project_id=#{params.project_id}/ref_types=#{params.ref_types}/list_mode=#{params.list_mode}/" end @spec refresh(LoadParams.t()) :: {:ok, t(), atom()} | {:error, String.t()} @@ -167,6 +171,7 @@ defmodule Front.ProjectPage.Model do project: project, page_token: params.page_token, direction: params.direction, + list_mode: params.list_mode, user_page?: params.user_page?, ref_types: params.ref_types, workflows: workflows, @@ -186,8 +191,16 @@ defmodule Front.ProjectPage.Model do defp first_page?(params), do: params.page_token == "" defp everyones_page?(params), do: params.user_page? == false + defp cacheable_mode?(params), do: (params.list_mode || "latest") == "latest" defp list_workflows(params) do + case params.list_mode do + "all_pipelines" -> list_workflows_keyset(params) + _ -> list_workflows_latest(params) + end + end + + defp list_workflows_latest(params) do list_params = [ page_size: 10, page_token: params.page_token, @@ -196,12 +209,7 @@ defmodule Front.ProjectPage.Model do git_ref_types: params.ref_types ] - list_params = - if params.user_page? do - list_params |> Keyword.merge(requester_id: params.user_id) - else - list_params - end + list_params = maybe_put_requester(list_params, params.user_page?, params.user_id) workflow_api_metric_name = if params.user_page? do @@ -222,4 +230,47 @@ defmodule Front.ProjectPage.Model do {workflows, next_page_token, previous_page_token} end + + defp list_workflows_keyset(params) do + direction = keyset_direction(params.direction) + + list_params = + [ + page_size: 10, + page_token: params.page_token, + direction: direction, + project_id: params.project_id, + git_ref_types: params.ref_types + ] + |> maybe_put_requester(params.user_page?, params.user_id) + |> Enum.reject(fn {_, value} -> is_nil(value) or value == "" end) + + workflow_api_metric_name = + if params.user_page? do + "project_page_model_list_keyset_by_me" + else + "project_page_model_list_keyset" + end + + {wfs, next_page_token, previous_page_token} = + Watchman.benchmark(workflow_api_metric_name, fn -> + Models.Workflow.list_keyset(list_params) + end) + + workflows = + Watchman.benchmark("project_page_model_decorate_workflows", fn -> + Decorators.Workflow.decorate_many(wfs) + end) + + {workflows, next_page_token, previous_page_token} + end + + defp maybe_put_requester(list_params, true, requester_id), + do: Keyword.merge(list_params, requester_id: requester_id) + + defp maybe_put_requester(list_params, _requester?, _requester_id), do: list_params + + defp keyset_direction("next"), do: KeysetDirection.value(:NEXT) + defp keyset_direction("previous"), do: KeysetDirection.value(:PREVIOUS) + defp keyset_direction(_), do: nil end diff --git a/front/lib/front_web/controllers/project_controller.ex b/front/lib/front_web/controllers/project_controller.ex index 40a349a74..ddcfee89f 100644 --- a/front/lib/front_web/controllers/project_controller.ex +++ b/front/lib/front_web/controllers/project_controller.ex @@ -107,6 +107,7 @@ defmodule FrontWeb.ProjectController do user_id: user_id, page_token: params["page_token"] || "", direction: params["direction"] || "", + list_mode: workflow_list_mode_setting(conn), user_page?: user_page?(conn), ref_types: ref_types(conn) ) @@ -819,6 +820,7 @@ defmodule FrontWeb.ProjectController do user_id: user_id, page_token: params["page_token"] || "", direction: params["direction"] || "", + list_mode: workflow_list_mode_setting(conn), user_page?: user_page?(conn), ref_types: ref_types(conn) ) @@ -841,6 +843,8 @@ defmodule FrontWeb.ProjectController do branches: model.branches, type: workflow_list_type_setting(conn), requester: model.user_page?, + listing: model.list_mode, + all_pipelines_enabled: show_all_pipelines?(conn), notice: conn |> get_flash(:notice), social_metatags: true, workflows: model.workflows @@ -887,6 +891,7 @@ defmodule FrontWeb.ProjectController do user_id: user_id, page_token: params["page_token"] || "", direction: params["direction"] || "", + list_mode: workflow_list_mode_setting(conn), user_page?: false, ref_types: ref_types(conn) ) @@ -898,7 +903,8 @@ defmodule FrontWeb.ProjectController do href: "/projects/#{project.id}/workflows", params: [ page_token: params.page_token, - direction: params.direction + direction: params.direction, + listing: data.list_mode || "latest" ] } @@ -915,6 +921,8 @@ defmodule FrontWeb.ProjectController do pollman: pollman, title: "#{project.name}・#{data.organization.name}", requester: data.user_page?, + listing: data.list_mode, + all_pipelines_enabled: show_all_pipelines?(conn), type: workflow_list_type_setting(conn), notice: conn |> get_flash(:notice), social_metatags: true @@ -938,7 +946,8 @@ defmodule FrontWeb.ProjectController do requester: user_page?(conn), page_token: conn.params["page_token"] || "", direction: conn.params["direction"] || "", - type: conn.params["type"] || memory["projectType"] + type: conn.params["type"] || memory["projectType"], + listing: model.list_mode || "latest" ] } end @@ -949,10 +958,32 @@ defmodule FrontWeb.ProjectController do conn.params["type"] || memory["projectType"] end + defp workflow_list_mode_setting(conn) do + memory = conn.req_cookies["memory"] |> MemoryCookie.values() + + requested_mode = conn.params["listing"] || memory["projectListing"] || "latest" + + if show_all_pipelines?(conn) do + normalize_list_mode(requested_mode) + else + "latest" + end + end + + defp normalize_list_mode("all_pipelines"), do: "all_pipelines" + defp normalize_list_mode(_), do: "latest" + + defp show_all_pipelines?(conn) do + FeatureProvider.feature_enabled?(:project_page_all_pipelines, + param: conn.assigns.organization_id + ) + end + defp filters(conn) do %{ type: workflow_list_type_setting(conn), - requester: user_page?(conn) + requester: user_page?(conn), + listing: workflow_list_mode_setting(conn) } end diff --git a/front/lib/front_web/templates/project/_tabs.html.eex b/front/lib/front_web/templates/project/_tabs.html.eex index 45ccb5db0..c6630f51b 100644 --- a/front/lib/front_web/templates/project/_tabs.html.eex +++ b/front/lib/front_web/templates/project/_tabs.html.eex @@ -60,7 +60,14 @@ + + <%= if @all_pipelines_enabled do %> + + <% end %> + <%= if @all_pipelines_enabled do %> + + <% end %>
diff --git a/front/test/front/project_page/model_test.exs b/front/test/front/project_page/model_test.exs index f1c529ead..edc184ce2 100644 --- a/front/test/front/project_page/model_test.exs +++ b/front/test/front/project_page/model_test.exs @@ -37,7 +37,7 @@ defmodule Front.ProjectPage.ModelTest do ) assert Model.cache_key(params) == - "#{Model.cache_prefix()}/#{Model.cache_version()}/project_id=1/ref_types=branchtag/" + "#{Model.cache_prefix()}/#{Model.cache_version()}/project_id=1/ref_types=branchtag/list_mode=latest/" end end From 1775cf6410e408f2a73162d18ac54b2a5e3958db Mon Sep 17 00:00:00 2001 From: hamir-suspect Date: Tue, 28 Oct 2025 10:18:43 +0100 Subject: [PATCH 2/4] fix(front): consolidate filter options into single dropdown --- front/assets/js/memory_cookie.js | 4 +- .../js/project/components/choose_select.js | 29 +++++++++++++ front/assets/js/workflow_list.js | 2 +- front/lib/front/memory_cookie.ex | 2 +- .../controllers/project_controller.ex | 21 +++++++--- .../templates/project/_tabs.html.eex | 41 +++++++++---------- .../templates/project/private.html.eex | 1 + .../templates/project/public.html.eex | 1 + .../templates/project/public/_tabs.html.eex | 37 +++++++++-------- 9 files changed, 89 insertions(+), 49 deletions(-) diff --git a/front/assets/js/memory_cookie.js b/front/assets/js/memory_cookie.js index bdae73bc0..26ea0a34e 100644 --- a/front/assets/js/memory_cookie.js +++ b/front/assets/js/memory_cookie.js @@ -31,8 +31,8 @@ export class MemoryCookie { rootSidebar: false, rootRequester: true, projectType: "", - projectListing: "latest", - projectRequester: false, + projectListing: "all_pipelines", + projectRequester: "false", logDark: false, logWrap: true, logLive: true, diff --git a/front/assets/js/project/components/choose_select.js b/front/assets/js/project/components/choose_select.js index b08aafba3..b730d5f0a 100644 --- a/front/assets/js/project/components/choose_select.js +++ b/front/assets/js/project/components/choose_select.js @@ -19,6 +19,11 @@ export class ChooseSelect { let value = e.target.value; let key = e.target.getAttribute("data-key") + if (key === "listing_requester") { + this.updateListingRequester(value) + return + } + MemoryCookie.set('project' + key.charAt(0).toUpperCase() + key.slice(1), value) var u = new Url; @@ -27,4 +32,28 @@ export class ChooseSelect { window.location.href = u.toString() }) } + + updateListingRequester(value) { + const selection = this.selectionFor(value) + + MemoryCookie.set('projectListing', selection.listing) + MemoryCookie.set('projectRequester', selection.requester) + + const url = new Url + url.query['listing'] = selection.listing + url.query['requester'] = selection.requester + + window.location.href = url.toString() + } + + selectionFor(value) { + switch (value) { + case 'all_by_me': + return { listing: 'all_pipelines', requester: 'true' } + case 'latest_per_branch': + return { listing: 'latest', requester: 'false' } + default: + return { listing: 'all_pipelines', requester: 'false' } + } + } } diff --git a/front/assets/js/workflow_list.js b/front/assets/js/workflow_list.js index 98ac85e6f..e420c170e 100644 --- a/front/assets/js/workflow_list.js +++ b/front/assets/js/workflow_list.js @@ -7,7 +7,7 @@ export var WorkflowList = { pagination: null, pollmanList: null, container: null, - queryParams: ['page_token', 'direction', 'date_from', 'date_to', 'author', 'listing'], + queryParams: ['page_token', 'direction', 'date_from', 'date_to', 'author', 'listing', 'requester'], init: function() { if(this.initiated === true) { return; } diff --git a/front/lib/front/memory_cookie.ex b/front/lib/front/memory_cookie.ex index 1d366c0fa..bf88448f6 100644 --- a/front/lib/front/memory_cookie.ex +++ b/front/lib/front/memory_cookie.ex @@ -18,7 +18,7 @@ defmodule Front.MemoryCookie do "rootSidebar" => false, "rootRequester" => true, "projectType" => "", - "projectListing" => "latest", + "projectListing" => "all_pipelines", "projectRequester" => "false", "logDark" => false, "logWrap" => true, diff --git a/front/lib/front_web/controllers/project_controller.ex b/front/lib/front_web/controllers/project_controller.ex index ddcfee89f..e19f398d9 100644 --- a/front/lib/front_web/controllers/project_controller.ex +++ b/front/lib/front_web/controllers/project_controller.ex @@ -844,6 +844,7 @@ defmodule FrontWeb.ProjectController do type: workflow_list_type_setting(conn), requester: model.user_page?, listing: model.list_mode, + listing_requester: combined_listing_requester(model.list_mode, model.user_page?), all_pipelines_enabled: show_all_pipelines?(conn), notice: conn |> get_flash(:notice), social_metatags: true, @@ -902,9 +903,10 @@ defmodule FrontWeb.ProjectController do state: "poll", href: "/projects/#{project.id}/workflows", params: [ + requester: data.user_page?, page_token: params.page_token, direction: params.direction, - listing: data.list_mode || "latest" + listing: data.list_mode || workflow_list_mode_setting(conn) ] } @@ -922,6 +924,7 @@ defmodule FrontWeb.ProjectController do title: "#{project.name}・#{data.organization.name}", requester: data.user_page?, listing: data.list_mode, + listing_requester: combined_listing_requester(data.list_mode, data.user_page?), all_pipelines_enabled: show_all_pipelines?(conn), type: workflow_list_type_setting(conn), notice: conn |> get_flash(:notice), @@ -947,7 +950,7 @@ defmodule FrontWeb.ProjectController do page_token: conn.params["page_token"] || "", direction: conn.params["direction"] || "", type: conn.params["type"] || memory["projectType"], - listing: model.list_mode || "latest" + listing: model.list_mode || workflow_list_mode_setting(conn) ] } end @@ -961,7 +964,7 @@ defmodule FrontWeb.ProjectController do defp workflow_list_mode_setting(conn) do memory = conn.req_cookies["memory"] |> MemoryCookie.values() - requested_mode = conn.params["listing"] || memory["projectListing"] || "latest" + requested_mode = conn.params["listing"] || memory["projectListing"] || "all_pipelines" if show_all_pipelines?(conn) do normalize_list_mode(requested_mode) @@ -980,13 +983,21 @@ defmodule FrontWeb.ProjectController do end defp filters(conn) do + list_mode = workflow_list_mode_setting(conn) + requester? = user_page?(conn) + %{ type: workflow_list_type_setting(conn), - requester: user_page?(conn), - listing: workflow_list_mode_setting(conn) + requester: requester?, + listing: list_mode, + listing_requester: combined_listing_requester(list_mode, requester?) } end + defp combined_listing_requester(_, true), do: "all_by_me" + defp combined_listing_requester("all_pipelines", _), do: "all_pipelines" + defp combined_listing_requester(_, _), do: "latest_per_branch" + defp ref_types(conn) do conn |> workflow_list_type_setting() |> String.split(",", trim: true) end diff --git a/front/lib/front_web/templates/project/_tabs.html.eex b/front/lib/front_web/templates/project/_tabs.html.eex index c6630f51b..1141bca3c 100644 --- a/front/lib/front_web/templates/project/_tabs.html.eex +++ b/front/lib/front_web/templates/project/_tabs.html.eex @@ -51,30 +51,27 @@
-
- + + + - <%= if @all_pipelines_enabled do %> - + + + + + + + + - <% end %> - -
- -
+
+ +
+
diff --git a/front/lib/front_web/templates/project/private.html.eex b/front/lib/front_web/templates/project/private.html.eex index 3ec343064..9071118c3 100644 --- a/front/lib/front_web/templates/project/private.html.eex +++ b/front/lib/front_web/templates/project/private.html.eex @@ -12,6 +12,7 @@ window.InjectedDataByBackend.AssetsPath = "<%= assets_path() %>" type: @type, requester: @requester, listing: @listing, + listing_requester: @listing_requester, permissions: @permissions, all_pipelines_enabled: @all_pipelines_enabled %> <%= render FrontWeb.DashboardView, "partials/_workflows.html", conn: @conn, workflows: @workflows, pagination: @pagination, pollman: @pollman, page: :project %> diff --git a/front/lib/front_web/templates/project/public.html.eex b/front/lib/front_web/templates/project/public.html.eex index 97346d3d5..dcb71dc5d 100644 --- a/front/lib/front_web/templates/project/public.html.eex +++ b/front/lib/front_web/templates/project/public.html.eex @@ -26,6 +26,7 @@ window.InjectedDataByBackend.AssetsPath = "<%= assets_path() %>" type: @type, requester: @requester, listing: @listing, + listing_requester: @listing_requester, all_pipelines_enabled: @all_pipelines_enabled %> <%= render FrontWeb.ProjectView, "public/_workflows.html", project: @project, conn: @conn, pollman: @pollman, workflows: @workflows, page: :project %> diff --git a/front/lib/front_web/templates/project/public/_tabs.html.eex b/front/lib/front_web/templates/project/public/_tabs.html.eex index 3f6b685fb..73151ccee 100644 --- a/front/lib/front_web/templates/project/public/_tabs.html.eex +++ b/front/lib/front_web/templates/project/public/_tabs.html.eex @@ -48,26 +48,27 @@
Showing most recent
-
- + + + - <%= if @all_pipelines_enabled do %> - + + + + + + + + - <% end %> -
- -
+
+ +
+
From 040068084ed1526be78944bc7d099ede8f5de67b Mon Sep 17 00:00:00 2001 From: hamir-suspect Date: Tue, 28 Oct 2025 10:33:57 +0100 Subject: [PATCH 3/4] fix(front): update expected cache keys --- .../front/project_page/cache_invalidator_test.exs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/front/test/front/project_page/cache_invalidator_test.exs b/front/test/front/project_page/cache_invalidator_test.exs index 07ef2bf2d..9ef545a02 100644 --- a/front/test/front/project_page/cache_invalidator_test.exs +++ b/front/test/front/project_page/cache_invalidator_test.exs @@ -29,10 +29,10 @@ defmodule Front.ProjectPage.CacheInvalidatorTest do pipeline: pipeline } do branch_cache_key = - "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=branch/" + "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=branch/list_mode=latest/" all_git_refs_cache_key = - "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=/" + "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=/list_mode=latest/" Cacheman.put( :front, @@ -64,16 +64,16 @@ defmodule Front.ProjectPage.CacheInvalidatorTest do describe "project_updated" do test "invalidates project page caches", %{organization: organization, project: project} do tag_cache_key = - "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=tag/" + "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=tag/list_mode=latest/" branch_cache_key = - "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=branch/" + "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=branch/list_mode=latest/" pr_cache_key = - "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=pr/" + "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=pr/list_mode=latest/" all_git_refs_cache_key = - "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=/" + "project_page_model/#{Model.cache_version()}/project_id=#{project.id}/ref_types=/list_mode=latest/" Cacheman.put( :front, From 296e7e24dfef3b39b8d4ff3f91bf5cadd912518d Mon Sep 17 00:00:00 2001 From: hamir-suspect Date: Tue, 28 Oct 2025 13:06:02 +0100 Subject: [PATCH 4/4] fix(front): updated copy in dropdown and clear pagination when changing list type --- .../js/project/components/choose_select.js | 2 ++ .../templates/project/_tabs.html.eex | 21 ++++++++++++++++--- .../templates/project/public/_tabs.html.eex | 21 ++++++++++++++++--- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/front/assets/js/project/components/choose_select.js b/front/assets/js/project/components/choose_select.js index b730d5f0a..aabff8e6c 100644 --- a/front/assets/js/project/components/choose_select.js +++ b/front/assets/js/project/components/choose_select.js @@ -42,6 +42,8 @@ export class ChooseSelect { const url = new Url url.query['listing'] = selection.listing url.query['requester'] = selection.requester + delete url.query['page_token'] + delete url.query['direction'] window.location.href = url.toString() } diff --git a/front/lib/front_web/templates/project/_tabs.html.eex b/front/lib/front_web/templates/project/_tabs.html.eex index 1141bca3c..d1ecfbca2 100644 --- a/front/lib/front_web/templates/project/_tabs.html.eex +++ b/front/lib/front_web/templates/project/_tabs.html.eex @@ -1,4 +1,15 @@