From 21da2d1425a99d7d525f9acff341aebfaf454d0c Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Tue, 28 Jan 2025 17:40:08 -0500 Subject: [PATCH 01/11] fix:test: generate snapshot for test so all properties are present --- .../controllers/detours_controller_test.exs | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/test/skate_web/controllers/detours_controller_test.exs b/test/skate_web/controllers/detours_controller_test.exs index bf8a8ac14..f3e057baa 100644 --- a/test/skate_web/controllers/detours_controller_test.exs +++ b/test/skate_web/controllers/detours_controller_test.exs @@ -460,24 +460,10 @@ defmodule SkateWeb.DetoursControllerTest do other_user = insert(:user) # Manually insert a detour by another user - Detours.upsert_from_snapshot(other_user.id, %{ - "context" => %{ - "route" => %{ - "id" => "23", - "name" => "23", - "directionNames" => %{ - "0" => "Outbound", - "1" => "Inbound" - } - }, - "routePattern" => %{ - "headsign" => "Headsign", - "directionId" => 0 - }, - "nearestIntersection" => "Street A & Avenue B", - "uuid" => 10 - } - }) + Detours.upsert_from_snapshot( + other_user.id, + build(:detour_snapshot) + ) conn = get(conn, ~p"/api/detours") From b94a470a2e5b101c205a173a6774adf0fbd38a91 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Wed, 25 Sep 2024 14:27:20 -0400 Subject: [PATCH 02/11] refactor(ex/detours+notifications): move detour specific `Notificaitons.Db.Detour.Queries` into `Skate.Detours.Db.Detour.Queries` --- lib/notifications/db/detour.ex | 51 +++++++----------------- lib/notifications/db/notification.ex | 3 +- lib/skate/detours/db/detour.ex | 59 ++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 38 deletions(-) diff --git a/lib/notifications/db/detour.ex b/lib/notifications/db/detour.ex index 6798531de..092250c28 100644 --- a/lib/notifications/db/detour.ex +++ b/lib/notifications/db/detour.ex @@ -67,6 +67,12 @@ defmodule Notifications.Db.Detour do from(d in Notifications.Db.Detour, as: :detour_notification, select_merge: d) end + def with_detour(query \\ base()) do + with_named_binding(query, :detour, fn query, binding -> + join(query, :left, [detour_notification: n], assoc(n, ^binding), as: ^binding) + end) + end + @doc """ Retrieves detour information for notifications from the `Notifications.Db.Detour` table @@ -77,7 +83,7 @@ defmodule Notifications.Db.Detour do ...> |> Notifications.Notification.create_activated_detour_notification_from_detour() ...> iex> all_detour_notifications = - ...> Notifications.Db.Detour.Queries.get_derived_info() + ...> Notifications.Db.Detour.Queries.select_detour_notification_info() ...> |> Skate.Repo.all() ...> iex> [ @@ -92,42 +98,13 @@ defmodule Notifications.Db.Detour do false """ - def get_derived_info(query \\ base()) do - from( - [detour_notification: dn] in query, - left_join: ad in assoc(dn, :detour), - as: :associated_detour, - select_merge: %{ - route: ad.state["context"]["route"]["name"], - origin: ad.state["context"]["routePattern"]["name"], - headsign: ad.state["context"]["routePattern"]["headsign"], - - # Ecto can't figure out how to index a JSON map via another JSON value - # because (in the ways it was tried) Ecto won't allow us to use the - # value from the associated detour, `ad`, as a value in the - # ["JSON path"](https://hexdocs.pm/ecto/Ecto.Query.API.html#json_extract_path/2). - # - # i.e., this - # ad.state["context"]["route"]["directionNames"][ - # ad.state["context"]["routePattern"]["directionId"] - # ] - # - # But, Postgres _is_ able to do this, _if_ we get the types correct. - # A JSON value in Postgres is either of type JSON or JSONB, but - # - indexing a JSON array requires an `INTEGER`, - # - accessing a JSON map, requires Postgres's `TEXT` type. - # - # So because we know the `directionId` will correspond to the keys in - # `directionNames`, casting the `directionId` to `TEXT` allows us to - # access the `directionNames` JSON map - direction: - fragment( - "? -> CAST(? AS TEXT)", - ad.state["context"]["route"]["directionNames"], - ad.state["context"]["routePattern"]["directionId"] - ) - } - ) + def select_detour_notification_info(query \\ base()) do + query + |> with_detour() + |> Skate.Detours.Db.Detour.Queries.select_route_name(:route) + |> Skate.Detours.Db.Detour.Queries.select_route_pattern_name(:origin) + |> Skate.Detours.Db.Detour.Queries.select_route_pattern_headsign(:headsign) + |> Skate.Detours.Db.Detour.Queries.select_direction(:direction) end end end diff --git a/lib/notifications/db/notification.ex b/lib/notifications/db/notification.ex index 0ef3e9852..ea181e6de 100644 --- a/lib/notifications/db/notification.ex +++ b/lib/notifications/db/notification.ex @@ -151,7 +151,8 @@ defmodule Notifications.Db.Notification do @spec select_detour_info() :: Ecto.Query.t() def select_detour_info(query \\ base()) do from([notification: n] in query, - left_join: detour in subquery(Notifications.Db.Detour.Queries.get_derived_info()), + left_join: + detour in subquery(Notifications.Db.Detour.Queries.select_detour_notification_info()), on: detour.id == n.detour_id, select_merge: %{ detour: detour diff --git a/lib/skate/detours/db/detour.ex b/lib/skate/detours/db/detour.ex index 9a8b32b55..777bc28af 100644 --- a/lib/skate/detours/db/detour.ex +++ b/lib/skate/detours/db/detour.ex @@ -24,4 +24,63 @@ defmodule Skate.Detours.Db.Detour do |> validate_required([:state]) |> foreign_key_constraint(:author_id) end + + defmodule Queries do + @moduledoc """ + Defines composable queries for retrieving `Skate.Detours.Db.Detour` + """ + + import Ecto.Query + + def base() do + # Select nothing (`[]`) at first so further `select_merge`'s + # don't have an issue + from(Skate.Detours.Db.Detour, as: :detour, select: []) + end + + def select_route_name(query \\ base(), key \\ :route_name) do + select_merge(query, [detour: d], %{^key => d.state["context"]["route"]["name"]}) + end + + def select_route_pattern_name(query \\ base(), key \\ :route_pattern_name) do + select_merge(query, [detour: d], %{^key => d.state["context"]["routePattern"]["name"]}) + end + + def select_route_pattern_headsign(query \\ base(), key \\ :headsign) do + select_merge(query, [detour: d], %{^key => d.state["context"]["routePattern"]["headsign"]}) + end + + def select_route_pattern_id(query \\ base(), key \\ :route_pattern_id) do + select_merge(query, [detour: d], %{^key => d.state["context"]["routePattern"]["id"]}) + end + + def select_direction(query \\ base(), key \\ :direction) do + select_merge(query, [detour: d], %{ + # Ecto can't figure out how to index a JSON map via another JSON value + # because (in the ways it was tried) Ecto won't allow us to use the + # value from the associated detour, `ad`, as a value in the + # ["JSON path"](https://hexdocs.pm/ecto/Ecto.Query.API.html#json_extract_path/2). + # + # i.e., this + # ad.state["context"]["route"]["directionNames"][ + # ad.state["context"]["routePattern"]["directionId"] + # ] + # + # But, Postgres _is_ able to do this, _if_ we get the types correct. + # A JSON value in Postgres is either of type JSON or JSONB, but + # - indexing a JSON array requires an `INTEGER`, + # - accessing a JSON map, requires Postgres's `TEXT` type. + # + # So because we know the `directionId` will correspond to the keys in + # `directionNames`, casting the `directionId` to `TEXT` allows us to + # access the `directionNames` JSON map + ^key => + fragment( + "? -> CAST(? AS TEXT)", + d.state["context"]["route"]["directionNames"], + d.state["context"]["routePattern"]["directionId"] + ) + }) + end + end end From ae260b3491e5e30c2834ceb2d8f628fca61b5f56 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Tue, 28 Jan 2025 18:03:15 -0500 Subject: [PATCH 03/11] fix:test: assert on ID rather than all properties --- test/skate/detours/db_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/skate/detours/db_test.exs b/test/skate/detours/db_test.exs index a0fa534a6..92ef4961d 100644 --- a/test/skate/detours/db_test.exs +++ b/test/skate/detours/db_test.exs @@ -14,8 +14,8 @@ defmodule Skate.Detours.DbTest do end test "list_detours/0 returns all detours" do - detour = detour_fixture() - assert Skate.Repo.preload(Detours.list_detours(), :author) == [detour] + %{id: id} = detour_fixture() + assert [%{id: ^id}] = Skate.Repo.preload(Detours.list_detours(), :author) end test "get_detour!/1 returns the detour with given id" do From 2c2c904ff2d9649635c9f307c27295d894418116 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Wed, 25 Sep 2024 14:27:20 -0400 Subject: [PATCH 04/11] feat(ex/detours): query Detour JSON in postgres wip! working, pr ready wip! add virtual fields to struct wip! add route id --- lib/skate/detours/db/detour.ex | 75 ++++++++++++++++++++++++++++++++++ lib/skate/detours/detour.ex | 27 ++++++++++++ lib/skate/detours/detours.ex | 17 ++++---- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/lib/skate/detours/db/detour.ex b/lib/skate/detours/db/detour.ex index 777bc28af..5317d3f24 100644 --- a/lib/skate/detours/db/detour.ex +++ b/lib/skate/detours/db/detour.ex @@ -16,6 +16,29 @@ defmodule Skate.Detours.Db.Detour do field :activated_at, :utc_datetime_usec timestamps() + + ## Detour virtual fields + # ------------------------------------------------------- + + # Route properties + field :route_id, :string, virtual: true + field :route_name, :string, virtual: true + field :route_pattern_id, :string, virtual: true + field :route_pattern_name, :string, virtual: true + field :headsign, :string, virtual: true + field :direction, :string, virtual: true + + # Default detour properties + field :nearest_intersection, :string, virtual: true + + # Activated properties + field :estimated_duration, :string, virtual: true + + # Temporary field to make querying the `:state` faster and avoid needing to + # pull the entire `:state` value + field :state_value, :map, virtual: true + + # ------------------------------------------------------- end def changeset(detour, attrs) do @@ -38,6 +61,42 @@ defmodule Skate.Detours.Db.Detour do from(Skate.Detours.Db.Detour, as: :detour, select: []) end + def sorted_by_last_updated(query \\ base()) do + order_by(query, desc: :updated_at) + end + + def with_author(query \\ base(), key \\ :author) do + from([detour: d] in query, + join: a in assoc(d, :author), + as: :author, + select_merge: %{^key => a} + ) + end + + def select_detour_list_info(query \\ base()) do + query + |> select_merge([ + :author_id, + :updated_at, + :id, + :activated_at + ]) + |> sorted_by_last_updated() + |> with_author() + |> select_state_value() + |> select_starting_intersection() + |> select_route_id() + |> select_route_name() + |> select_route_pattern_headsign() + |> select_direction() + |> select_estimated_duration() + |> select_route_pattern_id() + end + + def select_route_id(query \\ base(), key \\ :route_id) do + select_merge(query, [detour: d], %{^key => d.state["context"]["route"]["id"]}) + end + def select_route_name(query \\ base(), key \\ :route_name) do select_merge(query, [detour: d], %{^key => d.state["context"]["route"]["name"]}) end @@ -82,5 +141,21 @@ defmodule Skate.Detours.Db.Detour do ) }) end + + def select_starting_intersection(query \\ base(), key \\ :nearest_intersection) do + select_merge(query, [detour: d], %{ + ^key => d.state["context"]["nearestIntersection"] + }) + end + + def select_estimated_duration(query \\ base(), key \\ :estimated_duration) do + select_merge(query, [detour: d], %{ + ^key => d.state["context"]["selectedDuration"] + }) + end + + def select_state_value(query \\ base(), key \\ :state_value) do + select_merge(query, [detour: d], %{^key => %{"value" => d.state["value"]}}) + end end end diff --git a/lib/skate/detours/detour.ex b/lib/skate/detours/detour.ex index 6c317472d..8dd2e9908 100644 --- a/lib/skate/detours/detour.ex +++ b/lib/skate/detours/detour.ex @@ -66,6 +66,33 @@ defmodule Skate.Detours.Detour do } end + def from(status, %{ + id: id, + author_id: author_id, + updated_at: updated_at, + route_pattern_id: route_pattern_id, + route_name: route_name, + headsign: headsign, + nearest_intersection: nearest_intersection, + direction: direction + }) + when not is_nil(headsign) and + not is_nil(direction) and + not is_nil(route_name) and + not is_nil(nearest_intersection) do + %__MODULE__{ + id: id, + route: route_name, + via_variant: RoutePattern.via_variant(route_pattern_id), + direction: direction, + name: headsign, + intersection: nearest_intersection, + updated_at: timestamp_to_unix(updated_at), + author_id: author_id, + status: status + } + end + def from(_status, _attrs), do: nil # Converts the db timestamp to unix diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index 5d9c4922b..ec61cedf8 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -22,12 +22,7 @@ defmodule Skate.Detours.Detours do [%Detour{}, ...] """ def list_detours do - (detour in Skate.Detours.Db.Detour) - |> from( - preload: [:author], - order_by: [desc: detour.updated_at] - ) - |> Repo.all() + Repo.all(Skate.Detours.Db.Detour.Queries.select_detour_list_info()) end @doc """ @@ -41,7 +36,7 @@ defmodule Skate.Detours.Detours do def active_detours_by_route(route_id) do list_detours() |> Enum.filter(fn detour -> - categorize_detour(detour) == :active and get_detour_route_id(detour) == route_id + categorize_detour(detour) == :active and detour.route_id == route_id end) |> Enum.map(fn detour -> db_detour_to_detour(detour) end) end @@ -91,9 +86,9 @@ defmodule Skate.Detours.Detours do def db_detour_to_detour( :active, - %Detour{ + %{ activated_at: activated_at, - state: %{"context" => %{"selectedDuration" => estimated_duration}} + estimated_duration: estimated_duration } = db_detour ) do details = DetailedDetour.from(:active, db_detour) @@ -131,6 +126,10 @@ defmodule Skate.Detours.Detours do user """ @spec categorize_detour(detour :: map()) :: detour_type() + def categorize_detour(%{state_value: state_value}) when not is_nil(state_value) do + categorize_detour(%{state: state_value}) + end + def categorize_detour(%{state: %{"value" => %{"Detour Drawing" => %{"Active" => _}}}}), do: :active From ae08f98b3574ab01e9d04e004f2f4b35c07e7e14 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Mon, 27 Jan 2025 15:32:44 -0500 Subject: [PATCH 05/11] wip! replace `select_merge` with `select_fields` wip! make `with_fields` handle virtual fields wip! move split_fields into own function review! wip! replace `with_author` --- lib/skate/detours/db/detour.ex | 42 ++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/skate/detours/db/detour.ex b/lib/skate/detours/db/detour.ex index a1fa04eea..80b540e04 100644 --- a/lib/skate/detours/db/detour.ex +++ b/lib/skate/detours/db/detour.ex @@ -61,6 +61,28 @@ defmodule Skate.Detours.Db.Detour do from(Skate.Detours.Db.Detour, as: :detour, select: []) end + @doc """ + """ + def select_fields(query \\ base(), fields) when is_list(fields) do + %{virtual_fields: wanted_virtual_fields, fields: wanted_fields} = split_fields(fields) + + query + |> select_merge(^wanted_fields) + |> select_virtual_fields(wanted_virtual_fields) + end + + defp split_fields(input_fields) do + schema_virtual_fields = Skate.Detours.Db.Detour.__schema__(:virtual_fields) + + {virtual_fields, fields} = + Enum.split_with(input_fields, fn field -> field in schema_virtual_fields end) + + %{ + virtual_fields: virtual_fields, + fields: fields + } + end + def select_virtual_fields(query \\ base(), fields) when is_list(fields) do Enum.reduce(fields, query, fn field, query -> case field do @@ -92,13 +114,17 @@ defmodule Skate.Detours.Db.Detour do def select_detour_list_info(query \\ base()) do query - |> select_merge([ + |> preload([ + :author + ]) + |> select_fields([ + # Table Columns + :id, :author_id, + :activated_at, :updated_at, - :id, - :activated_at - ]) - |> select_virtual_fields([ + + # Virtual Fields :route_id, :route_name, :route_pattern_id, @@ -107,10 +133,12 @@ defmodule Skate.Detours.Db.Detour do :direction, :nearest_intersection, :estimated_duration, - :state_value + :state_value, + + # Nested Fields + author: [:email] ]) |> sorted_by_last_updated() - |> with_author() end def select_route_id(query \\ base(), key \\ :route_id) do From 0aaa3fed1d2c902c0a2f40a56893ba338781ea29 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Mon, 27 Jan 2025 15:32:44 -0500 Subject: [PATCH 06/11] feat: add `status` enum virtual field --- lib/skate/detours/db/detour.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/skate/detours/db/detour.ex b/lib/skate/detours/db/detour.ex index a1fa04eea..688910cea 100644 --- a/lib/skate/detours/db/detour.ex +++ b/lib/skate/detours/db/detour.ex @@ -19,6 +19,7 @@ defmodule Skate.Detours.Db.Detour do ## Detour virtual fields # ------------------------------------------------------- + field :status, Ecto.Enum, values: [:draft, :active, :past], virtual: true # Route properties field :route_id, :string, virtual: true From 529a87f37022246611e3aafcc1baf0559b724f31 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Wed, 29 Jan 2025 11:26:15 -0500 Subject: [PATCH 07/11] feat(ex/detours): add `fields` arg to `list_detours` --- lib/skate/detours/detours.ex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index ec61cedf8..a3d54cb8d 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -25,6 +25,12 @@ defmodule Skate.Detours.Detours do Repo.all(Skate.Detours.Db.Detour.Queries.select_detour_list_info()) end + def list_detours(fields) do + Skate.Detours.Db.Detour.Queries.select_fields(fields) + |> preload([:author]) + |> Repo.all() + end + @doc """ Returns the list of detours by route id with author, sorted by updated_at From 1e0abb3b82991fb450747dc3e92d425796e69c2b Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Wed, 29 Jan 2025 11:56:29 -0500 Subject: [PATCH 08/11] refactor: make `select_virtual_fields` a private implementation detail --- lib/skate/detours/db/detour.ex | 3 ++- lib/skate/detours/detours.ex | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/skate/detours/db/detour.ex b/lib/skate/detours/db/detour.ex index 56dd0c13b..626d5f743 100644 --- a/lib/skate/detours/db/detour.ex +++ b/lib/skate/detours/db/detour.ex @@ -63,6 +63,7 @@ defmodule Skate.Detours.Db.Detour do end @doc """ + Builds a query that _selects_ data from columns or extracts from the JSON `:state`. """ def select_fields(query \\ base(), fields) when is_list(fields) do %{virtual_fields: wanted_virtual_fields, fields: wanted_fields} = split_fields(fields) @@ -84,7 +85,7 @@ defmodule Skate.Detours.Db.Detour do } end - def select_virtual_fields(query \\ base(), fields) when is_list(fields) do + defp select_virtual_fields(query, fields) when is_list(fields) do Enum.reduce(fields, query, fn field, query -> case field do :route_id -> select_route_id(query) diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index a3d54cb8d..edff3b356 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -28,6 +28,7 @@ defmodule Skate.Detours.Detours do def list_detours(fields) do Skate.Detours.Db.Detour.Queries.select_fields(fields) |> preload([:author]) + |> Skate.Detours.Db.Detour.Queries.sorted_by_last_updated() |> Repo.all() end From db4e3caf94097fbe5dab839bf8746500f92a8a2d Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Fri, 31 Jan 2025 09:04:42 -0500 Subject: [PATCH 09/11] fix(ex/detour): use `with_author` instead of `preload` --- lib/skate/detours/db/detour.ex | 16 +++++++++++++--- lib/skate/detours/detours.ex | 2 +- .../controllers/detours_admin_controller.ex | 11 ++++++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/skate/detours/db/detour.ex b/lib/skate/detours/db/detour.ex index 626d5f743..ab69c5f16 100644 --- a/lib/skate/detours/db/detour.ex +++ b/lib/skate/detours/db/detour.ex @@ -106,11 +106,21 @@ defmodule Skate.Detours.Db.Detour do order_by(query, desc: :updated_at) end - def with_author(query \\ base(), key \\ :author) do + @doc """ + Joins the `Skate.Settings.Db.User` struct into the `Skate.Detours.Db.Detour` + via Ecto preload. + + > ### Primary Keys required in query when using `with_author/1` {:.warning} + > When preloading structs, Ecto requires that primary key fields are also + > queried on all preloaded structs. + > This means that when querying `:author` via `select_fields`, you need to + > explicitly request `:id` on both the `Skate.Detours.Db.Detour` and the + > `Skate.Settings.Db.User`. + """ + def with_author(query \\ base()) do from([detour: d] in query, join: a in assoc(d, :author), - as: :author, - select_merge: %{^key => a} + preload: [author: a] ) end diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index edff3b356..6be0f0acb 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -27,7 +27,7 @@ defmodule Skate.Detours.Detours do def list_detours(fields) do Skate.Detours.Db.Detour.Queries.select_fields(fields) - |> preload([:author]) + |> Skate.Detours.Db.Detour.Queries.with_author() |> Skate.Detours.Db.Detour.Queries.sorted_by_last_updated() |> Repo.all() end diff --git a/lib/skate_web/controllers/detours_admin_controller.ex b/lib/skate_web/controllers/detours_admin_controller.ex index c115dcf5a..928059872 100644 --- a/lib/skate_web/controllers/detours_admin_controller.ex +++ b/lib/skate_web/controllers/detours_admin_controller.ex @@ -24,9 +24,14 @@ defmodule SkateWeb.DetoursAdminController do # Status column; Required for categorizing the detour :state_value, - # For some reason, without the id explicitly present, we're not able to preload the association - :author_id, - author: [:email] + # For some reason, without the primary keys explicitly present in the + # query, we're not able to preload the association. So we need the + # `User.id` and `Detour.id` explicitly in the query. + :id, + author: [ + :email, + :id + ] ] |> Detours.list_detours() |> Enum.map(fn detour -> From 3304ae52cea95286114ea6a89e7186f585345024 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Fri, 31 Jan 2025 09:32:14 -0500 Subject: [PATCH 10/11] feat(ex/detours): add `add_author?/2` function and replace `with_author` --- lib/skate/detours/db/detour.ex | 17 ++++++++++++----- lib/skate/detours/detours.ex | 1 - 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/skate/detours/db/detour.ex b/lib/skate/detours/db/detour.ex index ab69c5f16..40a751294 100644 --- a/lib/skate/detours/db/detour.ex +++ b/lib/skate/detours/db/detour.ex @@ -19,7 +19,8 @@ defmodule Skate.Detours.Db.Detour do ## Detour virtual fields # ------------------------------------------------------- - field :status, Ecto.Enum, values: [:draft, :active, :past], virtual: true + field(:status, Ecto.Enum, values: [:draft, :active, :past], virtual: true) :: + simple_status() | nil # Route properties field :route_id, :string, virtual: true @@ -69,6 +70,7 @@ defmodule Skate.Detours.Db.Detour do %{virtual_fields: wanted_virtual_fields, fields: wanted_fields} = split_fields(fields) query + |> add_author?(wanted_fields) |> select_merge(^wanted_fields) |> select_virtual_fields(wanted_virtual_fields) end @@ -106,6 +108,14 @@ defmodule Skate.Detours.Db.Detour do order_by(query, desc: :updated_at) end + defp add_author?(query, fields) do + if Keyword.has_key?(fields, :author) do + with_author(query) + else + query + end + end + @doc """ Joins the `Skate.Settings.Db.User` struct into the `Skate.Detours.Db.Detour` via Ecto preload. @@ -126,9 +136,6 @@ defmodule Skate.Detours.Db.Detour do def select_detour_list_info(query \\ base()) do query - |> preload([ - :author - ]) |> select_fields([ # Table Columns :id, @@ -148,7 +155,7 @@ defmodule Skate.Detours.Db.Detour do :state_value, # Nested Fields - author: [:email] + author: [:email, :id] ]) |> sorted_by_last_updated() end diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index 6be0f0acb..5825d2419 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -27,7 +27,6 @@ defmodule Skate.Detours.Detours do def list_detours(fields) do Skate.Detours.Db.Detour.Queries.select_fields(fields) - |> Skate.Detours.Db.Detour.Queries.with_author() |> Skate.Detours.Db.Detour.Queries.sorted_by_last_updated() |> Repo.all() end From 45462f9f456f0c962b22b3f17692b4063d63c4d5 Mon Sep 17 00:00:00 2001 From: Kayla Firestack Date: Fri, 31 Jan 2025 13:26:09 -0500 Subject: [PATCH 11/11] refactor: move detour_status definition into `Db.Detour` --- lib/skate/detours/db/detour.ex | 4 +++- lib/skate/detours/detours.ex | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/skate/detours/db/detour.ex b/lib/skate/detours/db/detour.ex index 40a751294..9b3c911fe 100644 --- a/lib/skate/detours/db/detour.ex +++ b/lib/skate/detours/db/detour.ex @@ -8,6 +8,8 @@ defmodule Skate.Detours.Db.Detour do alias Skate.Settings.Db.User + @type detour_status :: :active | :draft | :past + typed_schema "detours" do field :state, :map belongs_to :author, User @@ -20,7 +22,7 @@ defmodule Skate.Detours.Db.Detour do ## Detour virtual fields # ------------------------------------------------------- field(:status, Ecto.Enum, values: [:draft, :active, :past], virtual: true) :: - simple_status() | nil + detour_status() | nil # Route properties field :route_id, :string, virtual: true diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index 5825d2419..19ac68031 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -122,7 +122,7 @@ defmodule Skate.Detours.Detours do nil end - @type detour_type :: :active | :draft | :past + @type detour_type :: Skate.Detours.Db.Detour.detour_status() @doc """ Takes a `Skate.Detours.Db.Detour` struct and a `Skate.Settings.Db.User` id