From d0d6e5e6f952b0e882a13304c6fa92a7722b5cae Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Thu, 12 Sep 2024 11:51:46 -0400 Subject: [PATCH 01/13] tweak: List of detours need uuids, to later retrieve specific detours by id --- assets/src/models/detour.ts | 3 +++ assets/tests/components/detourListPage.test.tsx | 4 ++++ lib/skate/detours/detours.ex | 3 +++ 3 files changed, 10 insertions(+) diff --git a/assets/src/models/detour.ts b/assets/src/models/detour.ts index 850f2e766..ef6736269 100644 --- a/assets/src/models/detour.ts +++ b/assets/src/models/detour.ts @@ -4,6 +4,7 @@ import { CreateDetourMachineInput } from "./createDetourMachine" import { array, Infer, nullable, number, string, type } from "superstruct" export const SimpleDetour = type({ + uuid: number(), route: string(), direction: string(), name: string(), @@ -14,6 +15,7 @@ export const SimpleDetour = type({ export type SimpleDetour = Infer export const SimpleDetourFromApi = type({ + uuid: number(), route: string(), direction: string(), name: string(), @@ -26,6 +28,7 @@ export type SimpleDetourFromApi = Infer const simpleDetourFromData = ( detourData: SimpleDetourFromApi ): SimpleDetour => ({ + uuid: detourData.uuid, route: detourData.route, direction: detourData.direction, name: detourData.name, diff --git a/assets/tests/components/detourListPage.test.tsx b/assets/tests/components/detourListPage.test.tsx index 5b2ec7e41..1c234cf6d 100644 --- a/assets/tests/components/detourListPage.test.tsx +++ b/assets/tests/components/detourListPage.test.tsx @@ -20,6 +20,7 @@ describe("DetourListPage", () => { Ok({ active: [ { + uuid: 1, route: "1", direction: "Inbound", name: "Headsign A", @@ -27,6 +28,7 @@ describe("DetourListPage", () => { updatedAt: 1724866392, }, { + uuid: 8, route: "2", direction: "Outbound", name: "Headsign B", @@ -37,6 +39,7 @@ describe("DetourListPage", () => { draft: null, past: [ { + uuid: 10, route: "1", direction: "Inbound", name: "Headsign A", @@ -44,6 +47,7 @@ describe("DetourListPage", () => { updatedAt: 1724866392, }, { + uuid: 7, route: "1", direction: "Outbound", name: "Headsign Z", diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index f8b08a748..7f4ac14d1 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -9,6 +9,7 @@ defmodule Skate.Detours.Detours do alias Skate.Settings.User @type t :: %__MODULE__{ + uuid: integer(), route: String.t(), direction: String.t(), name: String.t(), @@ -21,6 +22,7 @@ defmodule Skate.Detours.Detours do @derive Jason.Encoder defstruct [ + :uuid, :route, :direction, :name, @@ -95,6 +97,7 @@ defmodule Skate.Detours.Detours do |> DateTime.to_unix() %__MODULE__{ + uuid: db_detour.id, route: route_name, direction: direction, name: headsign, From fc226210ad98efdf21a72b7bbe2d1a334fe32127 Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Fri, 13 Sep 2024 12:15:38 -0400 Subject: [PATCH 02/13] feat: Stub out frontend API calls, reorganized models, renamed routes --- assets/src/api.ts | 18 ++++- assets/src/models/detour.ts | 68 +++++-------------- assets/src/models/detoursList.ts | 62 +++++++++++++++++ .../controllers/detours_controller.ex | 4 +- lib/skate_web/router.ex | 3 +- .../controllers/detours_controller_test.exs | 8 +-- 6 files changed, 102 insertions(+), 61 deletions(-) create mode 100644 assets/src/models/detoursList.ts diff --git a/assets/src/api.ts b/assets/src/api.ts index c616ce149..12e914891 100644 --- a/assets/src/api.ts +++ b/assets/src/api.ts @@ -53,8 +53,8 @@ import { } from "./models/locationSearchSuggestionData" import { LocationSearchSuggestion } from "./models/locationSearchSuggestion" import { DetourShapeData, detourShapeFromData } from "./models/detourShapeData" -import { groupedDetoursFromData, GroupedSimpleDetours } from "./models/detour" -import { DetourShape, FinishedDetour, UnfinishedDetour } from "./models/detour" +import { groupedDetoursFromData, GroupedSimpleDetours } from "./models/detoursList" +import { DetourShape, detourStateFromData, DetourWithState, FinishedDetour, UnfinishedDetour } from "./models/detour" import { FinishedDetourData, finishedDetourFromData, @@ -510,6 +510,8 @@ export const putRouteTabs = (routeTabs: RouteTab[]): Promise => body: JSON.stringify({ route_tabs: routeTabs }), }) +// #region Detour API functions + export const putDetourUpdate = ( snapshot: Snapshot ): Promise> => @@ -529,12 +531,22 @@ export const putDetourUpdate = ( export const fetchDetours = (): Promise> => apiCallResult({ - url: `/api/detours/get_detours`, + url: `/api/detours`, OkStruct: GroupedSimpleDetours, ErrStruct: never(), parser: groupedDetoursFromData, }) +export const fetchDetour = (id: number): Promise> => + apiCallResult({ + url: `/api/detours/${id}`, + OkStruct: DetourWithState, + ErrStruct: never(), + parser: detourStateFromData + }) + +// #endregion Detour API functions + const getCsrfToken = (): string => appData()?.csrfToken || "" export const nullableParser = diff --git a/assets/src/models/detour.ts b/assets/src/models/detour.ts index 850f2e766..86444511e 100644 --- a/assets/src/models/detour.ts +++ b/assets/src/models/detour.ts @@ -1,64 +1,30 @@ import { LatLngLiteral } from "leaflet" import { ShapePoint, Stop } from "../schedule" import { CreateDetourMachineInput } from "./createDetourMachine" -import { array, Infer, nullable, number, string, type } from "superstruct" +import { any, Infer, number, string, type } from "superstruct" -export const SimpleDetour = type({ - route: string(), - direction: string(), - name: string(), - intersection: string(), - updatedAt: number(), +export const DetourWithState = type({ + author: string(), + state: any(), + updatedAt: number() }) -export type SimpleDetour = Infer +export type DetourWithState = Infer -export const SimpleDetourFromApi = type({ - route: string(), - direction: string(), - name: string(), - intersection: string(), - updated_at: number(), +export const DetourWithStateFromApi = type({ + author: string(), + state: any(), + updated_at: number() }) -export type SimpleDetourFromApi = Infer +export type DetourWithStateFromApi = Infer -const simpleDetourFromData = ( - detourData: SimpleDetourFromApi -): SimpleDetour => ({ - route: detourData.route, - direction: detourData.direction, - name: detourData.name, - intersection: detourData.intersection, - updatedAt: detourData.updated_at, -}) - -export const GroupedSimpleDetours = type({ - active: nullable(array(SimpleDetour)), - draft: nullable(array(SimpleDetour)), - past: nullable(array(SimpleDetour)), -}) - -export type GroupedSimpleDetours = Infer - -export const GroupedDetoursFromApi = type({ - active: nullable(array(SimpleDetourFromApi)), - draft: nullable(array(SimpleDetourFromApi)), - past: nullable(array(SimpleDetourFromApi)), -}) - -export type GroupedDetoursFromApi = Infer - -export const groupedDetoursFromData = ( - groupedDetours: GroupedDetoursFromApi -): GroupedSimpleDetours => ({ - active: - groupedDetours.active?.map((detour) => simpleDetourFromData(detour)) || - null, - draft: - groupedDetours.draft?.map((detour) => simpleDetourFromData(detour)) || null, - past: - groupedDetours.past?.map((detour) => simpleDetourFromData(detour)) || null, +export const detourStateFromData = ( + detourWithState: DetourWithStateFromApi +): DetourWithState => ({ + author: detourWithState.author, + state: detourWithState.state, + updatedAt: detourWithState.updated_at }) export interface DetourShape { diff --git a/assets/src/models/detoursList.ts b/assets/src/models/detoursList.ts new file mode 100644 index 000000000..d8c2f6206 --- /dev/null +++ b/assets/src/models/detoursList.ts @@ -0,0 +1,62 @@ +import { array, Infer, nullable, number, string, type } from "superstruct" + +export const SimpleDetour = type({ + uuid: number(), + route: string(), + direction: string(), + name: string(), + intersection: string(), + updatedAt: number(), +}) + +export type SimpleDetour = Infer + +export const SimpleDetourFromApi = type({ + uuid: number(), + route: string(), + direction: string(), + name: string(), + intersection: string(), + updated_at: number(), +}) + +export type SimpleDetourFromApi = Infer + +export const simpleDetourFromData = ( + detourData: SimpleDetourFromApi +): SimpleDetour => ({ + uuid: detourData.uuid, + route: detourData.route, + direction: detourData.direction, + name: detourData.name, + intersection: detourData.intersection, + updatedAt: detourData.updated_at, +}) + +export const GroupedSimpleDetours = type({ + active: nullable(array(SimpleDetour)), + draft: nullable(array(SimpleDetour)), + past: nullable(array(SimpleDetour)), +}) + +export type GroupedSimpleDetours = Infer + +export const GroupedDetoursFromApi = type({ + active: nullable(array(SimpleDetourFromApi)), + draft: nullable(array(SimpleDetourFromApi)), + past: nullable(array(SimpleDetourFromApi)), +}) + +export type GroupedDetoursFromApi = Infer + +export const groupedDetoursFromData = ( + groupedDetours: GroupedDetoursFromApi +): GroupedSimpleDetours => ({ + active: + groupedDetours.active?.map((detour) => simpleDetourFromData(detour)) || + null, + draft: + groupedDetours.draft?.map((detour) => simpleDetourFromData(detour)) || null, + past: + groupedDetours.past?.map((detour) => simpleDetourFromData(detour)) || null, +}) \ No newline at end of file diff --git a/lib/skate_web/controllers/detours_controller.ex b/lib/skate_web/controllers/detours_controller.ex index 46971e5ae..5a30b2d22 100644 --- a/lib/skate_web/controllers/detours_controller.ex +++ b/lib/skate_web/controllers/detours_controller.ex @@ -24,8 +24,8 @@ defmodule SkateWeb.DetoursController do json(conn, %{data: returned_uuid}) end - @spec get_detours(Plug.Conn.t(), map()) :: Plug.Conn.t() - def get_detours(conn, _params) do + @spec detours(Plug.Conn.t(), map()) :: Plug.Conn.t() + def detours(conn, _params) do %{id: user_id} = AuthManager.Plug.current_resource(conn) detours = Detours.grouped_detours(user_id) diff --git a/lib/skate_web/router.ex b/lib/skate_web/router.ex index 26d95d328..3f5cd40ec 100644 --- a/lib/skate_web/router.ex +++ b/lib/skate_web/router.ex @@ -159,9 +159,10 @@ defmodule SkateWeb.Router do get "/location_search/place/:id", LocationSearchController, :get get "/location_search/search", LocationSearchController, :search get "/location_search/suggest", LocationSearchController, :suggest + get "/detours", DetoursController, :detours + get "/detours/:detour_id", DetoursController, :detour post "/detours/directions/", DetourRouteController, :directions put "/detours/update_snapshot", DetoursController, :update_snapshot - get "/detours/get_detours", DetoursController, :get_detours post "/detours/unfinished_detour", DetoursController, :unfinished_detour post "/detours/finished_detour", DetoursController, :finished_detour end diff --git a/test/skate_web/controllers/detours_controller_test.exs b/test/skate_web/controllers/detours_controller_test.exs index 713ec62a3..1cba71e05 100644 --- a/test/skate_web/controllers/detours_controller_test.exs +++ b/test/skate_web/controllers/detours_controller_test.exs @@ -58,7 +58,7 @@ defmodule SkateWeb.DetoursControllerTest do end end - describe "get_detours/2" do + describe "detours/2" do defp populate_db_and_get_user(conn) do # Active detour put(conn, "/api/detours/update_snapshot", %{ @@ -134,7 +134,7 @@ defmodule SkateWeb.DetoursControllerTest do test "fetches detours from database and groups by active, past, draft", %{conn: conn} do author_id = populate_db_and_get_user(conn) - conn = get(conn, "/api/detours/get_detours") + conn = get(conn, "/api/detours") assert %{ "data" => %{ @@ -204,7 +204,7 @@ defmodule SkateWeb.DetoursControllerTest do } }) - conn = get(conn, "/api/detours/get_detours") + conn = get(conn, "/api/detours") assert %{ "data" => %{ @@ -288,7 +288,7 @@ defmodule SkateWeb.DetoursControllerTest do } }) - conn = get(conn, "/api/detours/get_detours") + conn = get(conn, "/api/detours") assert %{ "data" => %{ From a54e487453ec8c9088cba799bfefb71556cf2809 Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Fri, 13 Sep 2024 12:16:34 -0400 Subject: [PATCH 03/13] tweak: formatting --- assets/src/api.ts | 19 +++++++++++++++---- assets/src/models/detour.ts | 6 +++--- assets/src/models/detoursList.ts | 2 +- lib/skate_web/router.ex | 2 +- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/assets/src/api.ts b/assets/src/api.ts index 12e914891..2e63e3886 100644 --- a/assets/src/api.ts +++ b/assets/src/api.ts @@ -53,8 +53,17 @@ import { } from "./models/locationSearchSuggestionData" import { LocationSearchSuggestion } from "./models/locationSearchSuggestion" import { DetourShapeData, detourShapeFromData } from "./models/detourShapeData" -import { groupedDetoursFromData, GroupedSimpleDetours } from "./models/detoursList" -import { DetourShape, detourStateFromData, DetourWithState, FinishedDetour, UnfinishedDetour } from "./models/detour" +import { + groupedDetoursFromData, + GroupedSimpleDetours, +} from "./models/detoursList" +import { + DetourShape, + detourStateFromData, + DetourWithState, + FinishedDetour, + UnfinishedDetour, +} from "./models/detour" import { FinishedDetourData, finishedDetourFromData, @@ -537,12 +546,14 @@ export const fetchDetours = (): Promise> => parser: groupedDetoursFromData, }) -export const fetchDetour = (id: number): Promise> => +export const fetchDetour = ( + id: number +): Promise> => apiCallResult({ url: `/api/detours/${id}`, OkStruct: DetourWithState, ErrStruct: never(), - parser: detourStateFromData + parser: detourStateFromData, }) // #endregion Detour API functions diff --git a/assets/src/models/detour.ts b/assets/src/models/detour.ts index 86444511e..e1d0a1d8b 100644 --- a/assets/src/models/detour.ts +++ b/assets/src/models/detour.ts @@ -6,7 +6,7 @@ import { any, Infer, number, string, type } from "superstruct" export const DetourWithState = type({ author: string(), state: any(), - updatedAt: number() + updatedAt: number(), }) export type DetourWithState = Infer @@ -14,7 +14,7 @@ export type DetourWithState = Infer export const DetourWithStateFromApi = type({ author: string(), state: any(), - updated_at: number() + updated_at: number(), }) export type DetourWithStateFromApi = Infer @@ -24,7 +24,7 @@ export const detourStateFromData = ( ): DetourWithState => ({ author: detourWithState.author, state: detourWithState.state, - updatedAt: detourWithState.updated_at + updatedAt: detourWithState.updated_at, }) export interface DetourShape { diff --git a/assets/src/models/detoursList.ts b/assets/src/models/detoursList.ts index d8c2f6206..cbca6b9a6 100644 --- a/assets/src/models/detoursList.ts +++ b/assets/src/models/detoursList.ts @@ -59,4 +59,4 @@ export const groupedDetoursFromData = ( groupedDetours.draft?.map((detour) => simpleDetourFromData(detour)) || null, past: groupedDetours.past?.map((detour) => simpleDetourFromData(detour)) || null, -}) \ No newline at end of file +}) diff --git a/lib/skate_web/router.ex b/lib/skate_web/router.ex index 3f5cd40ec..316873435 100644 --- a/lib/skate_web/router.ex +++ b/lib/skate_web/router.ex @@ -160,7 +160,7 @@ defmodule SkateWeb.Router do get "/location_search/search", LocationSearchController, :search get "/location_search/suggest", LocationSearchController, :suggest get "/detours", DetoursController, :detours - get "/detours/:detour_id", DetoursController, :detour + get "/detours/:detour_id", DetoursController, :detour post "/detours/directions/", DetourRouteController, :directions put "/detours/update_snapshot", DetoursController, :update_snapshot post "/detours/unfinished_detour", DetoursController, :unfinished_detour From 94d3cce279e605c11d35f15a48d3a736e3762491 Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Fri, 13 Sep 2024 12:17:51 -0400 Subject: [PATCH 04/13] fix: frontend reference to moved model --- assets/src/components/detoursTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/src/components/detoursTable.tsx b/assets/src/components/detoursTable.tsx index 4c615b16f..b50675802 100644 --- a/assets/src/components/detoursTable.tsx +++ b/assets/src/components/detoursTable.tsx @@ -3,7 +3,7 @@ import { Table } from "react-bootstrap" import { RoutePill } from "./routePill" import { useCurrentTimeSeconds } from "../hooks/useCurrentTime" import { timeAgoLabel } from "../util/dateTime" -import { SimpleDetour } from "../models/detour" +import { SimpleDetour } from "../models/detoursList" interface DetoursTableProps { data: SimpleDetour[] From 64cf86260ad024b04f173e3c8af433e8a4691d8b Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Fri, 13 Sep 2024 12:36:01 -0400 Subject: [PATCH 05/13] tweak: overcommitted stuff that should be in another branch --- assets/src/models/detoursList.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/assets/src/models/detoursList.ts b/assets/src/models/detoursList.ts index cbca6b9a6..597d7a642 100644 --- a/assets/src/models/detoursList.ts +++ b/assets/src/models/detoursList.ts @@ -1,7 +1,6 @@ import { array, Infer, nullable, number, string, type } from "superstruct" export const SimpleDetour = type({ - uuid: number(), route: string(), direction: string(), name: string(), @@ -12,7 +11,6 @@ export const SimpleDetour = type({ export type SimpleDetour = Infer export const SimpleDetourFromApi = type({ - uuid: number(), route: string(), direction: string(), name: string(), @@ -25,7 +23,6 @@ export type SimpleDetourFromApi = Infer export const simpleDetourFromData = ( detourData: SimpleDetourFromApi ): SimpleDetour => ({ - uuid: detourData.uuid, route: detourData.route, direction: detourData.direction, name: detourData.name, From 12e45db8b10cc8debc262cfe6b06127a2fb6d2e9 Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Fri, 13 Sep 2024 12:51:40 -0400 Subject: [PATCH 06/13] feat: Organized detour models, added new function to detour controller --- lib/skate/detours/detour.ex | 53 ++++++ lib/skate/detours/detours.ex | 97 +++++------ .../controllers/detours_controller.ex | 11 +- test/skate/detours/db_test.exs | 18 -- .../controllers/detours_controller_test.exs | 160 +++++++++++------- 5 files changed, 203 insertions(+), 136 deletions(-) create mode 100644 lib/skate/detours/detour.ex diff --git a/lib/skate/detours/detour.ex b/lib/skate/detours/detour.ex new file mode 100644 index 000000000..94aaab916 --- /dev/null +++ b/lib/skate/detours/detour.ex @@ -0,0 +1,53 @@ +defmodule Skate.Detours.Detour do + @moduledoc """ + Modules for different detour structures that can be read from the db + """ + + defmodule Detailed do + @moduledoc """ + Detailed detours have had the db detour state parsed into attributes + """ + @type t :: %__MODULE__{ + uuid: integer(), + route: String.t(), + direction: String.t(), + name: String.t(), + intersection: String.t(), + updated_at: integer(), + author_id: integer(), + status: :active | :draft | :past + } + + @derive Jason.Encoder + + defstruct [ + :uuid, + :route, + :direction, + :name, + :intersection, + :updated_at, + :author_id, + :status + ] + end + + defmodule WithState do + @moduledoc """ + Detours WithState have had their state left intact + """ + @type t :: %__MODULE__{ + author: String.t(), + state: map(), + updated_at: integer() + } + + @derive Jason.Encoder + + defstruct [ + :author, + :state, + :updated_at + ] + end +end diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index f8b08a748..b95d66584 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -6,30 +6,10 @@ defmodule Skate.Detours.Detours do import Ecto.Query, warn: false alias Skate.Repo alias Skate.Detours.Db.Detour + alias Skate.Detours.Detour.Detailed, as: DetailedDetour + alias Skate.Detours.Detour.WithState, as: DetourWithState alias Skate.Settings.User - @type t :: %__MODULE__{ - route: String.t(), - direction: String.t(), - name: String.t(), - intersection: String.t(), - updated_at: integer(), - author_id: integer(), - status: :active | :draft | :past - } - - @derive Jason.Encoder - - defstruct [ - :route, - :direction, - :name, - :intersection, - :updated_at, - :author_id, - :status - ] - @doc """ Returns the list of detours. @@ -51,11 +31,16 @@ defmodule Skate.Detours.Detours do iex> grouped_detours(my_user_id) %{ - active: [%Detour{}, ...], + active: [%DetailedDetour{}, ...], draft: nil, - past: [%Detour{}, ...] + past: [%DetailedDetour{}, ...] } """ + @spec grouped_detours(integer()) :: %{ + active: list(DetailedDetour.t()) | nil, + draft: list(DetailedDetour.t()) | nil, + past: list(DetailedDetour.t()) | nil + } def grouped_detours(user_id) do detours = Repo.all( @@ -74,7 +59,7 @@ defmodule Skate.Detours.Detours do } end - @spec db_detour_to_detour(Detour.t(), integer()) :: t() + @spec db_detour_to_detour(Detour.t(), integer()) :: DetailedDetour.t() defp db_detour_to_detour( %{ state: %{ @@ -89,17 +74,12 @@ defmodule Skate.Detours.Detours do ) do direction = Map.get(direction_names, Integer.to_string(direction_id)) - date = - db_detour.updated_at - |> DateTime.from_naive!("Etc/UTC") - |> DateTime.to_unix() - - %__MODULE__{ + %DetailedDetour{ route: route_name, direction: direction, name: headsign, intersection: nearest_intersection, - updated_at: date, + updated_at: timestamp_to_unix(db_detour.updated_at), author_id: db_detour.author_id, status: categorize_detour(db_detour, user_id) } @@ -141,6 +121,34 @@ defmodule Skate.Detours.Detours do """ def get_detour!(id), do: Repo.get!(Detour, id) + @doc """ + Gets a single detour with state intact. + + Raises `Ecto.NoResultsError` if the Detour does not exist. + + ## Examples + + iex> get_detour_with_state!(123) + %DetourWithState{} + + iex> get_detour_with_state!(456) + ** (Ecto.NoResultsError) + + """ + @spec get_detour_with_state!(integer()) :: DetourWithState.t() + def get_detour_with_state!(id) do + detour = + Detour + |> Repo.get!(id) + |> Repo.preload(:author) + + %DetourWithState{ + state: detour.state, + updated_at: timestamp_to_unix(detour.updated_at), + author: detour.author.email + } + end + @doc """ Creates a detour. @@ -192,24 +200,6 @@ defmodule Skate.Detours.Detours do end end - @doc """ - Updates a detour. - - ## Examples - - iex> update_detour(detour, %{field: new_value}) - {:ok, %Detour{}} - - iex> update_detour(detour, %{field: bad_value}) - {:error, %Ecto.Changeset{}} - - """ - def update_detour(%Detour{} = detour, attrs) do - detour - |> Detour.changeset(attrs) - |> Repo.update() - end - @doc """ Deletes a detour. @@ -238,4 +228,11 @@ defmodule Skate.Detours.Detours do def change_detour(%Detour{} = detour, attrs \\ %{}) do Detour.changeset(detour, attrs) end + + # Converts the db timestamp to unix + defp timestamp_to_unix(db_date) do + db_date + |> DateTime.from_naive!("Etc/UTC") + |> DateTime.to_unix() + end end diff --git a/lib/skate_web/controllers/detours_controller.ex b/lib/skate_web/controllers/detours_controller.ex index 5a30b2d22..2ee170fb6 100644 --- a/lib/skate_web/controllers/detours_controller.ex +++ b/lib/skate_web/controllers/detours_controller.ex @@ -24,15 +24,20 @@ defmodule SkateWeb.DetoursController do json(conn, %{data: returned_uuid}) end + @spec detour(Plug.Conn.t(), map()) :: Plug.Conn.t() + def detour(conn, %{"detour_id" => detour_id}) do + detour = Detours.get_detour_with_state!(detour_id) + + json(conn, %{data: detour}) + end + @spec detours(Plug.Conn.t(), map()) :: Plug.Conn.t() def detours(conn, _params) do %{id: user_id} = AuthManager.Plug.current_resource(conn) detours = Detours.grouped_detours(user_id) - json(conn, %{ - data: detours - }) + json(conn, %{data: detours}) end @spec unfinished_detour(Plug.Conn.t(), map()) :: Plug.Conn.t() diff --git a/test/skate/detours/db_test.exs b/test/skate/detours/db_test.exs index 6d9d67a11..ea66c6281 100644 --- a/test/skate/detours/db_test.exs +++ b/test/skate/detours/db_test.exs @@ -37,24 +37,6 @@ defmodule Skate.Detours.DbTest do assert {:error, %Ecto.Changeset{}} = Detours.create_detour(@invalid_attrs) end - test "update_detour/2 with valid data updates the detour" do - detour = detour_fixture() - update_attrs = %{state: %{}} - - assert {:ok, %Detour{} = detour} = Detours.update_detour(detour, update_attrs) - assert detour.state == %{} - end - - test "update_detour/2 with invalid data returns error changeset" do - detour = detour_fixture() - assert {:error, %Ecto.Changeset{}} = Detours.update_detour(detour, @invalid_attrs) - - assert detour == - detour.id - |> Detours.get_detour!() - |> Skate.Repo.preload(:author) - end - test "delete_detour/1 deletes the detour" do detour = detour_fixture() assert {:ok, %Detour{}} = Detours.delete_detour(detour) diff --git a/test/skate_web/controllers/detours_controller_test.exs b/test/skate_web/controllers/detours_controller_test.exs index 1cba71e05..06cb2e830 100644 --- a/test/skate_web/controllers/detours_controller_test.exs +++ b/test/skate_web/controllers/detours_controller_test.exs @@ -58,78 +58,108 @@ defmodule SkateWeb.DetoursControllerTest do end end - describe "detours/2" do - defp populate_db_and_get_user(conn) do - # Active detour - put(conn, "/api/detours/update_snapshot", %{ - "snapshot" => %{ - "context" => %{ - "route" => %{ - "name" => "23", - "directionNames" => %{ - "0" => "Outbound", - "1" => "Inbound" - } - }, - "routePattern" => %{ - "headsign" => "Headsign", - "directionId" => 0 - }, - "nearestIntersection" => "Street A & Avenue B", - "uuid" => 1 + defp populate_db_and_get_user(conn) do + # Active detour + put(conn, "/api/detours/update_snapshot", %{ + "snapshot" => %{ + "context" => %{ + "route" => %{ + "name" => "23", + "directionNames" => %{ + "0" => "Outbound", + "1" => "Inbound" + } }, - "value" => %{"Detour Drawing" => "Active"} - } - }) - - # Past detour - put(conn, "/api/detours/update_snapshot", %{ - "snapshot" => %{ - "context" => %{ - "route" => %{ - "name" => "47", - "directionNames" => %{ - "0" => "Outbound", - "1" => "Inbound" - } - }, - "routePattern" => %{ - "headsign" => "Headsign", - "directionId" => 1 - }, - "nearestIntersection" => "Street C & Avenue D", - "uuid" => 2 + "routePattern" => %{ + "headsign" => "Headsign", + "directionId" => 0 + }, + "nearestIntersection" => "Street A & Avenue B", + "uuid" => 1 + }, + "value" => %{"Detour Drawing" => "Active"} + } + }) + + # Past detour + put(conn, "/api/detours/update_snapshot", %{ + "snapshot" => %{ + "context" => %{ + "route" => %{ + "name" => "47", + "directionNames" => %{ + "0" => "Outbound", + "1" => "Inbound" + } + }, + "routePattern" => %{ + "headsign" => "Headsign", + "directionId" => 1 + }, + "nearestIntersection" => "Street C & Avenue D", + "uuid" => 2 + }, + "value" => %{"Detour Drawing" => "Past"} + } + }) + + # Draft detour + put(conn, "/api/detours/update_snapshot", %{ + "snapshot" => %{ + "context" => %{ + "route" => %{ + "name" => "75", + "directionNames" => %{ + "0" => "Outbound", + "1" => "Inbound" + } }, - "value" => %{"Detour Drawing" => "Past"} + "routePattern" => %{ + "headsign" => "Headsign", + "directionId" => 0 + }, + "nearestIntersection" => "Street Y & Avenue Z", + "uuid" => 3 } - }) + } + }) - # Draft detour - put(conn, "/api/detours/update_snapshot", %{ - "snapshot" => %{ - "context" => %{ - "route" => %{ - "name" => "75", - "directionNames" => %{ - "0" => "Outbound", - "1" => "Inbound" - } - }, - "routePattern" => %{ - "headsign" => "Headsign", - "directionId" => 0 - }, - "nearestIntersection" => "Street Y & Avenue Z", - "uuid" => 3 - } - } - }) + 1 + |> Detours.get_detour!() + |> Map.get(:author_id) + end + + describe "detour/2" do + @tag :authenticated + test "fetches single detour with its state from database", %{conn: conn} do + populate_db_and_get_user(conn) + + conn = get(conn, "/api/detours/1") - 1 - |> Detours.get_detour!() - |> Map.get(:author_id) + json_response(conn, 200) + + assert %{ + "data" => %{ + "author" => "test_user@test.com", + "state" => %{ + "context" => %{ + "nearestIntersection" => "Street A & Avenue B", + "route" => %{ + "directionNames" => %{"0" => "Outbound", "1" => "Inbound"}, + "name" => "23" + }, + "routePattern" => %{"directionId" => 0, "headsign" => "Headsign"}, + "uuid" => 1 + }, + "value" => %{"Detour Drawing" => "Active"} + }, + "updated_at" => _ + } + } = json_response(conn, 200) end + end + describe "detours/2" do @tag :authenticated test "fetches detours from database and groups by active, past, draft", %{conn: conn} do author_id = populate_db_and_get_user(conn) From dd1a3896b0dc6851bb2aafd437485b34ed6133dd Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Wed, 18 Sep 2024 11:50:18 -0400 Subject: [PATCH 07/13] tweak: Refer to URL's with ~p --- test/skate_web/controllers/detours_controller_test.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/skate_web/controllers/detours_controller_test.exs b/test/skate_web/controllers/detours_controller_test.exs index 1cba71e05..1ff1438fc 100644 --- a/test/skate_web/controllers/detours_controller_test.exs +++ b/test/skate_web/controllers/detours_controller_test.exs @@ -134,7 +134,7 @@ defmodule SkateWeb.DetoursControllerTest do test "fetches detours from database and groups by active, past, draft", %{conn: conn} do author_id = populate_db_and_get_user(conn) - conn = get(conn, "/api/detours") + conn = get(conn, ~p"/api/detours") assert %{ "data" => %{ @@ -204,7 +204,7 @@ defmodule SkateWeb.DetoursControllerTest do } }) - conn = get(conn, "/api/detours") + conn = get(conn, ~p"/api/detours") assert %{ "data" => %{ @@ -288,7 +288,7 @@ defmodule SkateWeb.DetoursControllerTest do } }) - conn = get(conn, "/api/detours") + conn = get(conn, ~p"/api/detours") assert %{ "data" => %{ From 5eadaa3130e203effe0c1d3bcfa4666ebdd67dc6 Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Wed, 18 Sep 2024 13:40:22 -0400 Subject: [PATCH 08/13] fix: Fixed frontend models for detour api calls --- assets/src/api.ts | 12 ++-- assets/src/models/detour.ts | 18 +++--- assets/src/models/detoursList.ts | 58 ++++++++----------- .../tests/components/detourListPage.test.tsx | 2 +- 4 files changed, 40 insertions(+), 50 deletions(-) diff --git a/assets/src/api.ts b/assets/src/api.ts index 2e63e3886..81e2a0005 100644 --- a/assets/src/api.ts +++ b/assets/src/api.ts @@ -54,6 +54,7 @@ import { import { LocationSearchSuggestion } from "./models/locationSearchSuggestion" import { DetourShapeData, detourShapeFromData } from "./models/detourShapeData" import { + GroupedDetoursData, groupedDetoursFromData, GroupedSimpleDetours, } from "./models/detoursList" @@ -61,6 +62,7 @@ import { DetourShape, detourStateFromData, DetourWithState, + DetourWithStateData, FinishedDetour, UnfinishedDetour, } from "./models/detour" @@ -541,20 +543,18 @@ export const putDetourUpdate = ( export const fetchDetours = (): Promise> => apiCallResult({ url: `/api/detours`, - OkStruct: GroupedSimpleDetours, + OkStruct: GroupedDetoursData, ErrStruct: never(), - parser: groupedDetoursFromData, - }) + }).then((v) => map(v, groupedDetoursFromData)) export const fetchDetour = ( id: number ): Promise> => apiCallResult({ url: `/api/detours/${id}`, - OkStruct: DetourWithState, + OkStruct: DetourWithStateData, ErrStruct: never(), - parser: detourStateFromData, - }) + }).then((v) => map(v, detourStateFromData)) // #endregion Detour API functions diff --git a/assets/src/models/detour.ts b/assets/src/models/detour.ts index e1d0a1d8b..ec1955772 100644 --- a/assets/src/models/detour.ts +++ b/assets/src/models/detour.ts @@ -3,24 +3,22 @@ import { ShapePoint, Stop } from "../schedule" import { CreateDetourMachineInput } from "./createDetourMachine" import { any, Infer, number, string, type } from "superstruct" -export const DetourWithState = type({ - author: string(), - state: any(), - updatedAt: number(), -}) - -export type DetourWithState = Infer +export interface DetourWithState { + author: string + state: any + updatedAt: number +} -export const DetourWithStateFromApi = type({ +export const DetourWithStateData = type({ author: string(), state: any(), updated_at: number(), }) -export type DetourWithStateFromApi = Infer +export type DetourWithStateData = Infer export const detourStateFromData = ( - detourWithState: DetourWithStateFromApi + detourWithState: DetourWithStateData ): DetourWithState => ({ author: detourWithState.author, state: detourWithState.state, diff --git a/assets/src/models/detoursList.ts b/assets/src/models/detoursList.ts index 597d7a642..e2066291b 100644 --- a/assets/src/models/detoursList.ts +++ b/assets/src/models/detoursList.ts @@ -1,16 +1,14 @@ import { array, Infer, nullable, number, string, type } from "superstruct" -export const SimpleDetour = type({ - route: string(), - direction: string(), - name: string(), - intersection: string(), - updatedAt: number(), -}) - -export type SimpleDetour = Infer - -export const SimpleDetourFromApi = type({ +export interface SimpleDetour { + route: string + direction: string + name: string + intersection: string + updatedAt: number +} + +export const SimpleDetourData = type({ route: string(), direction: string(), name: string(), @@ -18,10 +16,10 @@ export const SimpleDetourFromApi = type({ updated_at: number(), }) -export type SimpleDetourFromApi = Infer +export type SimpleDetourData = Infer export const simpleDetourFromData = ( - detourData: SimpleDetourFromApi + detourData: SimpleDetourData ): SimpleDetour => ({ route: detourData.route, direction: detourData.direction, @@ -30,30 +28,24 @@ export const simpleDetourFromData = ( updatedAt: detourData.updated_at, }) -export const GroupedSimpleDetours = type({ - active: nullable(array(SimpleDetour)), - draft: nullable(array(SimpleDetour)), - past: nullable(array(SimpleDetour)), -}) - -export type GroupedSimpleDetours = Infer +export interface GroupedSimpleDetours { + active?: SimpleDetour[] + draft?: SimpleDetour[] + past?: SimpleDetour[] +} -export const GroupedDetoursFromApi = type({ - active: nullable(array(SimpleDetourFromApi)), - draft: nullable(array(SimpleDetourFromApi)), - past: nullable(array(SimpleDetourFromApi)), +export const GroupedDetoursData = type({ + active: nullable(array(SimpleDetourData)), + draft: nullable(array(SimpleDetourData)), + past: nullable(array(SimpleDetourData)), }) -export type GroupedDetoursFromApi = Infer +export type GroupedDetoursData = Infer export const groupedDetoursFromData = ( - groupedDetours: GroupedDetoursFromApi + groupedDetours: GroupedDetoursData ): GroupedSimpleDetours => ({ - active: - groupedDetours.active?.map((detour) => simpleDetourFromData(detour)) || - null, - draft: - groupedDetours.draft?.map((detour) => simpleDetourFromData(detour)) || null, - past: - groupedDetours.past?.map((detour) => simpleDetourFromData(detour)) || null, + active: groupedDetours.active?.map((detour) => simpleDetourFromData(detour)), + draft: groupedDetours.draft?.map((detour) => simpleDetourFromData(detour)), + past: groupedDetours.past?.map((detour) => simpleDetourFromData(detour)), }) diff --git a/assets/tests/components/detourListPage.test.tsx b/assets/tests/components/detourListPage.test.tsx index 5b2ec7e41..bf4ded599 100644 --- a/assets/tests/components/detourListPage.test.tsx +++ b/assets/tests/components/detourListPage.test.tsx @@ -34,7 +34,7 @@ describe("DetourListPage", () => { updatedAt: 1724856392, }, ], - draft: null, + draft: undefined, past: [ { route: "1", From 32acb8c12e277a4b3c38caa2fcf146bc88554b6e Mon Sep 17 00:00:00 2001 From: Hannah Purcell <69368883+hannahpurcell@users.noreply.github.com> Date: Wed, 18 Sep 2024 13:56:22 -0400 Subject: [PATCH 09/13] tweak: consolidate detour db query Co-authored-by: Kayla Firestack --- lib/skate/detours/detours.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index 30e943f55..e31cb61ab 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -139,8 +139,7 @@ defmodule Skate.Detours.Detours do @spec get_detour_with_state!(integer()) :: DetourWithState.t() def get_detour_with_state!(id) do detour = - Detour - |> Repo.get!(id) + get_detour!(id) |> Repo.preload(:author) %DetourWithState{ From 114143e076969f8eb0d22539938ba8661c0d2105 Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Wed, 18 Sep 2024 14:42:21 -0400 Subject: [PATCH 10/13] fix: replace uuid with id --- assets/src/models/detoursList.ts | 6 +++--- lib/skate/detours/detour.ex | 4 ++-- lib/skate/detours/detours.ex | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/assets/src/models/detoursList.ts b/assets/src/models/detoursList.ts index 39627ea6d..5e18f2a2a 100644 --- a/assets/src/models/detoursList.ts +++ b/assets/src/models/detoursList.ts @@ -1,7 +1,7 @@ import { array, Infer, nullable, number, string, type } from "superstruct" export interface SimpleDetour { - uuid: number + id: number route: string direction: string name: string @@ -10,7 +10,7 @@ export interface SimpleDetour { } export const SimpleDetourData = type({ - uuid: number(), + id: number(), route: string(), direction: string(), name: string(), @@ -23,7 +23,7 @@ export type SimpleDetourData = Infer export const simpleDetourFromData = ( detourData: SimpleDetourData ): SimpleDetour => ({ - uuid: detourData.uuid, + id: detourData.id, route: detourData.route, direction: detourData.direction, name: detourData.name, diff --git a/lib/skate/detours/detour.ex b/lib/skate/detours/detour.ex index 94aaab916..53581c82c 100644 --- a/lib/skate/detours/detour.ex +++ b/lib/skate/detours/detour.ex @@ -8,7 +8,7 @@ defmodule Skate.Detours.Detour do Detailed detours have had the db detour state parsed into attributes """ @type t :: %__MODULE__{ - uuid: integer(), + id: integer(), route: String.t(), direction: String.t(), name: String.t(), @@ -21,7 +21,7 @@ defmodule Skate.Detours.Detour do @derive Jason.Encoder defstruct [ - :uuid, + :id, :route, :direction, :name, diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index e31cb61ab..12f2bb816 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -75,7 +75,7 @@ defmodule Skate.Detours.Detours do direction = Map.get(direction_names, Integer.to_string(direction_id)) %DetailedDetour{ - uuid: db_detour.id, + id: db_detour.id, route: route_name, direction: direction, name: headsign, From 35dceeecdb58d628d9219579d4834d4c51438574 Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Wed, 18 Sep 2024 14:46:54 -0400 Subject: [PATCH 11/13] fix: missed some uuids --- assets/src/models/detoursList.ts | 2 +- assets/tests/components/detourListPage.test.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/src/models/detoursList.ts b/assets/src/models/detoursList.ts index 5e18f2a2a..92a331eda 100644 --- a/assets/src/models/detoursList.ts +++ b/assets/src/models/detoursList.ts @@ -1,7 +1,7 @@ import { array, Infer, nullable, number, string, type } from "superstruct" export interface SimpleDetour { - id: number + id: number route: string direction: string name: string diff --git a/assets/tests/components/detourListPage.test.tsx b/assets/tests/components/detourListPage.test.tsx index b4c96e85d..e4c761cbf 100644 --- a/assets/tests/components/detourListPage.test.tsx +++ b/assets/tests/components/detourListPage.test.tsx @@ -20,7 +20,7 @@ describe("DetourListPage", () => { Ok({ active: [ { - uuid: 1, + id: 1, route: "1", direction: "Inbound", name: "Headsign A", @@ -28,7 +28,7 @@ describe("DetourListPage", () => { updatedAt: 1724866392, }, { - uuid: 8, + id: 8, route: "2", direction: "Outbound", name: "Headsign B", @@ -39,7 +39,7 @@ describe("DetourListPage", () => { draft: undefined, past: [ { - uuid: 10, + id: 10, route: "1", direction: "Inbound", name: "Headsign A", @@ -47,7 +47,7 @@ describe("DetourListPage", () => { updatedAt: 1724866392, }, { - uuid: 7, + id: 7, route: "1", direction: "Outbound", name: "Headsign Z", From 713ba13dcda9cf1db09646380961d51c1bd27ff1 Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Wed, 18 Sep 2024 14:51:09 -0400 Subject: [PATCH 12/13] fix: credo --- lib/skate/detours/detours.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/skate/detours/detours.ex b/lib/skate/detours/detours.ex index 12f2bb816..8ea129039 100644 --- a/lib/skate/detours/detours.ex +++ b/lib/skate/detours/detours.ex @@ -139,7 +139,8 @@ defmodule Skate.Detours.Detours do @spec get_detour_with_state!(integer()) :: DetourWithState.t() def get_detour_with_state!(id) do detour = - get_detour!(id) + id + |> get_detour!() |> Repo.preload(:author) %DetourWithState{ From f4e546efbeff69d2728651c0aae60ee09d89f4f2 Mon Sep 17 00:00:00 2001 From: Hannah Purcell Date: Mon, 23 Sep 2024 10:32:20 -0400 Subject: [PATCH 13/13] fix: Merge adjustment --- .../controllers/detours_controller_test.exs | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/test/skate_web/controllers/detours_controller_test.exs b/test/skate_web/controllers/detours_controller_test.exs index 90de2da34..641e29097 100644 --- a/test/skate_web/controllers/detours_controller_test.exs +++ b/test/skate_web/controllers/detours_controller_test.exs @@ -58,25 +58,24 @@ defmodule SkateWeb.DetoursControllerTest do end end - describe "detours/2" do - defp populate_db_and_get_user(conn) do - # Active detour - put(conn, "/api/detours/update_snapshot", %{ - "snapshot" => %{ - "context" => %{ - "route" => %{ - "name" => "23", - "directionNames" => %{ - "0" => "Outbound", - "1" => "Inbound" - } - }, - "routePattern" => %{ - "headsign" => "Headsign", - "directionId" => 0 - }, - "nearestIntersection" => "Street A & Avenue B", - "uuid" => 1 + defp populate_db_and_get_user(conn) do + # Active detour + put(conn, "/api/detours/update_snapshot", %{ + "snapshot" => %{ + "context" => %{ + "route" => %{ + "name" => "23", + "directionNames" => %{ + "0" => "Outbound", + "1" => "Inbound" + } + }, + "routePattern" => %{ + "headsign" => "Headsign", + "directionId" => 0 + }, + "nearestIntersection" => "Street A & Avenue B", + "uuid" => 1 }, "value" => %{"Detour Drawing" => "Active"} }