From 33744f2fb650123cdc60a7e9c44e004f1d298394 Mon Sep 17 00:00:00 2001 From: Cristen Jones Date: Tue, 7 Jan 2025 11:12:46 -0500 Subject: [PATCH] feat(Live.TripPlanner): support /from and /to shortcuts (#2306) --- lib/dotcom/trip_plan/anti_corruption_layer.ex | 24 +++++++++ lib/dotcom_web/live/trip_planner.ex | 18 ++++++- lib/dotcom_web/router.ex | 2 + .../trip_plan/anti_corruption_layer_test.exs | 49 ++++++++++++++++++- 4 files changed, 90 insertions(+), 3 deletions(-) diff --git a/lib/dotcom/trip_plan/anti_corruption_layer.ex b/lib/dotcom/trip_plan/anti_corruption_layer.ex index 66df465166..ab907c7414 100644 --- a/lib/dotcom/trip_plan/anti_corruption_layer.ex +++ b/lib/dotcom/trip_plan/anti_corruption_layer.ex @@ -8,6 +8,30 @@ defmodule Dotcom.TripPlan.AntiCorruptionLayer do We ignore datetime_type and datetime and allow those to be set to 'now' and the current time respectively. """ + @location_service Application.compile_env!(:dotcom, :location_service) + + @doc """ + Given a query for the old trip planner /to or /from actions, replicate the old + behavior by searching for a location and using the first result. Convert this + to the new trip planner form values. + """ + def convert_old_action(action) do + with [key] when key in [:from, :to] <- Map.keys(action), + query when is_binary(query) <- Map.get(action, key), + {:ok, [%LocationService.Address{} = geocoded | _]} <- @location_service.geocode(query) do + %{ + "plan" => %{ + "#{key}_latitude" => geocoded.latitude, + "#{key}_longitude" => geocoded.longitude, + "#{key}" => geocoded.formatted + } + } + else + _ -> + %{"plan" => %{}} + end + end + @doc """ Given the params from the old trip planner, convert them to the new trip planner form values. diff --git a/lib/dotcom_web/live/trip_planner.ex b/lib/dotcom_web/live/trip_planner.ex index 1b7c2bc651..1b1a4402bd 100644 --- a/lib/dotcom_web/live/trip_planner.ex +++ b/lib/dotcom_web/live/trip_planner.ex @@ -39,8 +39,16 @@ defmodule DotcomWeb.Live.TripPlanner do - Clean any query parameters and convert them to a changeset for the input form. - Then, submit the form if the changeset is valid (i.e., the user visited with valid query parameters). """ - def mount(params, _session, socket) do - changeset = query_params_to_changeset(params) + def mount(params, _session, %{assigns: %{live_action: live_action}} = socket) do + changeset = + if is_atom(live_action) and is_binary(params["place"]) do + # Handle the /to/:place or /from/:place situation + live_action + |> action_to_query_params(params["place"]) + |> query_params_to_changeset() + else + query_params_to_changeset(params) + end new_socket = socket @@ -355,6 +363,12 @@ defmodule DotcomWeb.Live.TripPlanner do Timex.shift(datetime, minutes: added_minutes) end + defp action_to_query_params(action_key, action_value) do + %{} + |> Map.put(action_key, action_value) + |> AntiCorruptionLayer.convert_old_action() + end + # Convert query parameters to a changeset for the input form. # Use an anti corruption layer to convert old query parameters to new ones. defp query_params_to_changeset(params) do diff --git a/lib/dotcom_web/router.ex b/lib/dotcom_web/router.ex index e993fcf159..f693dccf6e 100644 --- a/lib/dotcom_web/router.ex +++ b/lib/dotcom_web/router.ex @@ -269,6 +269,8 @@ defmodule DotcomWeb.Router do live_session :rider, layout: {DotcomWeb.LayoutView, :preview} do live("/trip-planner", Live.TripPlanner) + live("/trip-planner/from/:place", Live.TripPlanner, :from) + live("/trip-planner/to/:place", Live.TripPlanner, :to) end end diff --git a/test/dotcom/trip_plan/anti_corruption_layer_test.exs b/test/dotcom/trip_plan/anti_corruption_layer_test.exs index 665620b910..b56dc6de8d 100644 --- a/test/dotcom/trip_plan/anti_corruption_layer_test.exs +++ b/test/dotcom/trip_plan/anti_corruption_layer_test.exs @@ -1,7 +1,54 @@ defmodule Dotcom.TripPlan.AntiCorruptionLayerTest do use ExUnit.Case - import Dotcom.TripPlan.AntiCorruptionLayer, only: [convert_old_params: 1] + import Dotcom.TripPlan.AntiCorruptionLayer, only: [convert_old_action: 1, convert_old_params: 1] + import Mox + import Test.Support.Factories.LocationService.LocationService + + setup :verify_on_exit! + + describe "convert_old_action/1" do + test "returns all defaults when no params are given" do + assert convert_old_action(%{}) == convert_old_action(%{"plan" => %{}}) + end + + test "returns params representing successfully geocoded result" do + query = Faker.Address.street_address() + geocoded_result = build(:address) + + expect(LocationService.Mock, :geocode, fn ^query -> + {:ok, [geocoded_result]} + end) + + assert convert_old_action(%{from: query}) == %{ + "plan" => %{ + "from" => geocoded_result.formatted, + "from_latitude" => geocoded_result.latitude, + "from_longitude" => geocoded_result.longitude + } + } + end + + test "returns all defaults when no address found" do + query = Faker.Address.street_address() + + expect(LocationService.Mock, :geocode, fn _ -> + {:ok, []} + end) + + assert convert_old_action(%{from: query}) == %{"plan" => %{}} + end + + test "returns all defaults for geocoding error" do + query = Faker.Address.street_address() + + expect(LocationService.Mock, :geocode, fn _ -> + {:error, :internal_error} + end) + + assert convert_old_action(%{from: query}) == %{"plan" => %{}} + end + end describe "convert_old_params/1" do test "returns all defaults when no params are given" do