Deployment and lifecycle management for Nix
0
fork

Configure Feed

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

server: add tree live, relationship to seed and some management functions

+312 -1
+2 -1
lib/sower.ex
··· 2 2 use Ash.Domain 3 3 4 4 resources do 5 + resource Sower.Inputs.Repository 5 6 resource Sower.Seed 6 - resource Sower.Inputs.Repository 7 + resource Sower.Tree 7 8 end 8 9 end
+16
lib/sower/seed.ex
··· 66 66 sort: [updated_at: :desc] 67 67 ) 68 68 end 69 + 70 + read :by_path do 71 + argument :out_path, :string do 72 + allow_nil? false 73 + end 74 + 75 + # # only return one 76 + # get? true 77 + # 78 + prepare build( 79 + filter: expr(out_path == ^arg(:out_path)), 80 + limit: 1, 81 + sort: [updated_at: :desc] 82 + ) 83 + end 69 84 end 70 85 71 86 attributes do ··· 95 110 96 111 code_interface do 97 112 define :by_id, args: [:id] 113 + define :by_path, args: [:out_path] 98 114 define :new, args: [:name, :type, :out_path, :branch, :repo_url] 99 115 define :new_legacy, args: [:name, :type, :out_path] 100 116 define :latest, args: [:name, :type]
+77
lib/sower/tree.ex
··· 1 + defmodule Sower.Tree do 2 + use Ash.Resource, 3 + data_layer: AshPostgres.DataLayer, 4 + domain: Sower 5 + 6 + @types [:nixos, :"home-manager", :"nix-darwin"] 7 + 8 + actions do 9 + defaults [:read] 10 + 11 + create :register do 12 + accept [:name, :type] 13 + end 14 + 15 + read :by_id do 16 + argument :id, :uuid do 17 + allow_nil? false 18 + end 19 + 20 + # only return one 21 + get? true 22 + 23 + filter expr(id == ^arg(:id)) 24 + end 25 + 26 + update :set_seed do 27 + require_atomic? false 28 + 29 + argument :seed_id, :uuid do 30 + allow_nil? false 31 + end 32 + 33 + change manage_relationship(:seed_id, :seed, type: :append_and_remove) 34 + end 35 + end 36 + 37 + attributes do 38 + uuid_primary_key :id 39 + create_timestamp :inserted_at 40 + update_timestamp :updated_at 41 + 42 + attribute :name, :string do 43 + allow_nil? false 44 + public? true 45 + end 46 + 47 + attribute :type, :atom do 48 + allow_nil? false 49 + public? true 50 + constraints one_of: @types 51 + end 52 + end 53 + 54 + code_interface do 55 + define :by_id, args: [:id] 56 + define :set_seed, args: [:seed_id] 57 + define :read_all, action: :read 58 + define :register, args: [:name, :type] 59 + end 60 + 61 + identities do 62 + identity :tree, [:name, :type] 63 + end 64 + 65 + postgres do 66 + table "trees" 67 + repo Sower.Repo 68 + 69 + references do 70 + reference :seed 71 + end 72 + end 73 + 74 + relationships do 75 + belongs_to :seed, Sower.Seed 76 + end 77 + end
+8
lib/sower_web/live/tree_live/index.ex
··· 1 + defmodule SowerWeb.TreeLive.Index do 2 + use SowerWeb, :live_view 3 + 4 + @impl true 5 + def mount(_params, _session, socket) do 6 + {:ok, stream(socket, :trees, Sower.Tree.read_all!())} 7 + end 8 + end
+18
lib/sower_web/live/tree_live/index.html.heex
··· 1 + <.header> 2 + Listing Trees 3 + <:subtitle>A tree is an individual client that grows from seeds.</:subtitle> 4 + </.header> 5 + 6 + <.table 7 + id="trees" 8 + rows={@streams.trees} 9 + row_click={fn {_id, tree} -> JS.navigate(~p"/trees/#{tree}") end} 10 + > 11 + <:col :let={{_id, tree}} label="name"><%= tree.name %></:col> 12 + <:col :let={{_id, tree}} label="type"><%= tree.type %></:col> 13 + <:action :let={{_id, tree}}> 14 + <div class="sr-only"> 15 + <.link navigate={~p"/trees/#{tree}"}>Show</.link> 16 + </div> 17 + </:action> 18 + </.table>
+18
lib/sower_web/live/tree_live/show.ex
··· 1 + defmodule SowerWeb.TreeLive.Show do 2 + use SowerWeb, :live_view 3 + 4 + @impl true 5 + def mount(_params, _session, socket) do 6 + {:ok, socket} 7 + end 8 + 9 + @impl true 10 + def handle_params(%{"id" => id}, _, socket) do 11 + {:noreply, 12 + socket 13 + |> assign(:page_title, page_title(socket.assigns.live_action)) 14 + |> assign(:tree, Sower.Tree.by_id!(id))} 15 + end 16 + 17 + defp page_title(:show), do: "Show Tree" 18 + end
+19
lib/sower_web/live/tree_live/show.html.heex
··· 1 + <.header> 2 + Tree <%= @tree.id %> 3 + <:subtitle>This is a tree record from your database.</:subtitle> 4 + </.header> 5 + 6 + <.list></.list> 7 + 8 + <.back navigate={~p"/trees"}>Back to trees</.back> 9 + 10 + <.modal :if={@live_action == :edit} id="tree-modal" show on_cancel={JS.patch(~p"/trees/#{@tree}")}> 11 + <.live_component 12 + module={SowerWeb.TreeLive.FormComponent} 13 + id={@tree.id} 14 + title={@page_title} 15 + action={@live_action} 16 + tree={@tree} 17 + patch={~p"/trees/#{@tree}"} 18 + /> 19 + </.modal>
+2
lib/sower_web/router.ex
··· 22 22 23 23 live("/seeds", SeedLive.Index, :index) 24 24 live("/seeds/:id", SeedLive.Show, :show) 25 + live("/trees", TreeLive.Index, :index) 26 + live("/trees/:id", TreeLive.Show, :show) 25 27 live("/inputs/repos", RepositoryLive.Index, :index) 26 28 live("/inputs/repos/:id", RepositoryLive.Show, :show) 27 29 end
+44
priv/repo/migrations/20240501030457_add_tree.exs
··· 1 + defmodule Sower.Repo.Migrations.AddTree do 2 + @moduledoc """ 3 + Updates resources based on their most recent snapshots. 4 + 5 + This file was autogenerated with `mix ash_postgres.generate_migrations` 6 + """ 7 + 8 + use Ecto.Migration 9 + 10 + def up do 11 + create table(:trees, primary_key: false) do 12 + add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true 13 + 14 + add :inserted_at, :utc_datetime_usec, 15 + null: false, 16 + default: fragment("(now() AT TIME ZONE 'utc')") 17 + 18 + add :updated_at, :utc_datetime_usec, 19 + null: false, 20 + default: fragment("(now() AT TIME ZONE 'utc')") 21 + 22 + add :name, :text, null: false 23 + add :type, :text, null: false 24 + 25 + add :seed_id, 26 + references(:seeds, 27 + column: :id, 28 + name: "trees_seed_id_fkey", 29 + type: :uuid, 30 + prefix: "public" 31 + ) 32 + end 33 + 34 + create unique_index(:trees, [:name, :type], name: "trees_tree_index") 35 + end 36 + 37 + def down do 38 + drop_if_exists unique_index(:trees, [:name, :type], name: "trees_tree_index") 39 + 40 + drop constraint(:trees, "trees_seed_id_fkey") 41 + 42 + drop table(:trees) 43 + end 44 + end
+108
priv/resource_snapshots/repo/trees/20240501030457.json
··· 1 + { 2 + "attributes": [ 3 + { 4 + "allow_nil?": false, 5 + "default": "fragment(\"gen_random_uuid()\")", 6 + "generated?": false, 7 + "primary_key?": true, 8 + "references": null, 9 + "size": null, 10 + "source": "id", 11 + "type": "uuid" 12 + }, 13 + { 14 + "allow_nil?": false, 15 + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", 16 + "generated?": false, 17 + "primary_key?": false, 18 + "references": null, 19 + "size": null, 20 + "source": "inserted_at", 21 + "type": "utc_datetime_usec" 22 + }, 23 + { 24 + "allow_nil?": false, 25 + "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", 26 + "generated?": false, 27 + "primary_key?": false, 28 + "references": null, 29 + "size": null, 30 + "source": "updated_at", 31 + "type": "utc_datetime_usec" 32 + }, 33 + { 34 + "allow_nil?": false, 35 + "default": "nil", 36 + "generated?": false, 37 + "primary_key?": false, 38 + "references": null, 39 + "size": null, 40 + "source": "name", 41 + "type": "text" 42 + }, 43 + { 44 + "allow_nil?": false, 45 + "default": "nil", 46 + "generated?": false, 47 + "primary_key?": false, 48 + "references": null, 49 + "size": null, 50 + "source": "type", 51 + "type": "text" 52 + }, 53 + { 54 + "allow_nil?": true, 55 + "default": "nil", 56 + "generated?": false, 57 + "primary_key?": false, 58 + "references": { 59 + "deferrable": false, 60 + "destination_attribute": "id", 61 + "destination_attribute_default": null, 62 + "destination_attribute_generated": null, 63 + "match_type": null, 64 + "match_with": null, 65 + "multitenancy": { 66 + "attribute": null, 67 + "global": null, 68 + "strategy": null 69 + }, 70 + "name": "trees_seed_id_fkey", 71 + "on_delete": null, 72 + "on_update": null, 73 + "primary_key?": true, 74 + "schema": "public", 75 + "table": "seeds" 76 + }, 77 + "size": null, 78 + "source": "seed_id", 79 + "type": "uuid" 80 + } 81 + ], 82 + "base_filter": null, 83 + "check_constraints": [], 84 + "custom_indexes": [], 85 + "custom_statements": [], 86 + "has_create_action": true, 87 + "hash": "2F9D6A28A880042D6A481F6105496943BE2DDD6AF91448A058147B0E3CF2161A", 88 + "identities": [ 89 + { 90 + "all_tenants?": false, 91 + "base_filter": null, 92 + "index_name": "trees_tree_index", 93 + "keys": [ 94 + "name", 95 + "type" 96 + ], 97 + "name": "tree" 98 + } 99 + ], 100 + "multitenancy": { 101 + "attribute": null, 102 + "global": null, 103 + "strategy": null 104 + }, 105 + "repo": "Elixir.Sower.Repo", 106 + "schema": null, 107 + "table": "trees" 108 + }