Deployment and lifecycle management for Nix
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(gardens): sortable Deploy column via lateral join

Name the lateral-join binding `:latest_deployment` and expose
`deploy_result` as a Flop join_field sourced from it, then wire the
Gardens index Deploy column to that field so headers become sort links.

Sort order is the raw enum string (alphabetic: failure, partial,
success), not by severity. Gardens with no deployment use Postgres
defaults (NULLs last on ASC, first on DESC).

sow-178

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+44 -3
+6 -2
apps/sower/lib/sower/orchestration/garden.ex
··· 14 14 @derive { 15 15 Flop.Schema, 16 16 filterable: [], 17 - sortable: [:name, :inserted_at, :version], 17 + sortable: [:name, :inserted_at, :version, :deploy_result], 18 18 default_limit: 20, 19 19 default_order: %{ 20 20 order_by: [:name], 21 21 order_directions: [:asc] 22 - } 22 + }, 23 + adapter_opts: [ 24 + join_fields: [deploy_result: [binding: :latest_deployment, field: :result]] 25 + ] 23 26 } 24 27 25 28 schema "gardens" do ··· 78 81 from(a in __MODULE__, 79 82 as: :garden, 80 83 left_lateral_join: d in subquery(latest_deployment_query), 84 + as: :latest_deployment, 81 85 on: true, 82 86 select: %{a | latest_deployment: d} 83 87 )
+1 -1
apps/sower/lib/sower_web/live/garden_live/index.html.heex
··· 41 41 <:col :let={garden} :if={:online in @visible_cols} label="Online"> 42 42 <.online state={garden.sid in Map.keys(@garden_presence)} /> 43 43 </:col> 44 - <:col :let={garden} :if={:deploy in @visible_cols} label="Deploy"> 44 + <:col :let={garden} :if={:deploy in @visible_cols} label="Deploy" field={:deploy_result}> 45 45 <.result result={garden.latest_deployment && garden.latest_deployment.result} /> 46 46 </:col> 47 47 <:col :let={garden} :if={:version in @visible_cols} label="Version" field={:version}>
+37
apps/sower/test/sower_web/live/garden_live_index_test.exs
··· 99 99 assert html =~ "9.9.9" 100 100 end 101 101 102 + test "Deploy column header is a sort link", %{conn: conn, user: user} do 103 + Sower.Repo.put_org_id(user.org_id) 104 + garden_fixture() 105 + 106 + {:ok, live, _html} = live(conn, ~p"/gardens") 107 + 108 + assert has_element?(live, "th a", "Deploy") 109 + end 110 + 111 + test "sorting by Deploy orders by latest deployment result", %{conn: conn, user: user} do 112 + Sower.Repo.put_org_id(user.org_id) 113 + 114 + g_failure = garden_fixture(%{name: "g-failure"}) 115 + g_success = garden_fixture(%{name: "g-success"}) 116 + 117 + {:ok, _} = 118 + Sower.Orchestration.create_deployment(%{ 119 + garden_id: g_failure.id, 120 + result: :failure, 121 + seeds: [], 122 + subscriptions: [] 123 + }) 124 + 125 + {:ok, _} = 126 + Sower.Orchestration.create_deployment(%{ 127 + garden_id: g_success.id, 128 + result: :success, 129 + seeds: [], 130 + subscriptions: [] 131 + }) 132 + 133 + {:ok, _live, html} = 134 + live(conn, ~p"/gardens?order_by[]=deploy_result&order_directions[]=asc") 135 + 136 + assert html =~ ~r/g-failure.*g-success/s 137 + end 138 + 102 139 test "sort preserved when column hidden then re-shown", %{conn: conn, user: user} do 103 140 Sower.Repo.put_org_id(user.org_id) 104 141 garden_fixture(%{name: "a", version: "2.0"})