From eee491f871091e5c593ddccb68c0bb65c66b05ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jadrian=20Sardi=C3=B1as?= Date: Tue, 9 Jun 2026 15:30:34 -0400 Subject: [PATCH 1/3] Update asdf in CI actions to >=0.16.0. --- .github/dependabot.yml | 4 ++++ .github/workflows/elixir.yml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index efed2e32b..0e9a82833 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,10 @@ updates: schedule: interval: daily open-pull-requests-limit: 10 +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" - package-ecosystem: mix directory: "/apps/api_accounts" schedule: diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index e2f01c4d8..1f414bc41 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -18,15 +18,15 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - # cache the ASDF directory, using the values from .tool-versions + # Cache the ASDF directory, using the values from .tool-versions. - name: ASDF cache uses: actions/cache@v4 with: path: ~/.asdf key: ${{ runner.os }}-asdf-v3-${{ hashFiles('.tool-versions') }} id: asdf-cache - # only run `asdf install` if we didn't hit the cache - - uses: asdf-vm/actions/install@v1 + # Only run `asdf install` if we didn't hit the cache. + - uses: asdf-vm/actions/install@b7bcd026f18772e44fe1026d729e1611cc435d47 # a.k.a. @4.0.1 if: steps.asdf-cache.outputs.cache-hit != 'true' - run: | mix local.rebar --force From ff800609e7ed2b0da5b565c362daf6b67677fbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jadrian=20Sardi=C3=B1as?= Date: Tue, 9 Jun 2026 15:43:05 -0400 Subject: [PATCH 2/3] chore: Bump credo from 1.7.14 to 1.7.19. --- mix.exs | 2 +- mix.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index 9c55dac9b..f8c5dd414 100644 --- a/mix.exs +++ b/mix.exs @@ -59,7 +59,7 @@ defmodule ApiUmbrella.Mixfile do defp deps do # Static analysis and style checking [ - {:credo, ">= 0.0.0", only: [:dev, :test], runtime: false}, + {:credo, ">= 1.7.19", only: [:dev, :test], runtime: false}, {:lcov_ex, "~> 0.2", only: [:dev, :test], runtime: false}, {:mix_audit, "~> 2.0", only: [:dev], runtime: false}, # Generate docs with `mix docs` diff --git a/mix.lock b/mix.lock index f04c47500..225dbe852 100644 --- a/mix.lock +++ b/mix.lock @@ -12,8 +12,8 @@ "comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "corsica": {:hex, :corsica, "2.1.3", "dccd094ffce38178acead9ae743180cdaffa388f35f0461ba1e8151d32e190e6", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "616c08f61a345780c2cf662ff226816f04d8868e12054e68963e95285b5be8bc"}, - "credo": {:hex, :credo, "1.7.14", "c7e75216cea8d978ba8c60ed9dede4cc79a1c99a266c34b3600dd2c33b96bc92", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "12a97d6bb98c277e4fb1dff45aaf5c137287416009d214fb46e68147bd9e0203"}, - "decimal": {:hex, :decimal, "3.1.0", "9ede268cff827e6f0c4fb1b34747c82630dce5d7b877dfb22ec8f0cb25855fce", [:mix], [], "hexpm", "e8b3efb3bb3a13cb5e4268ffe128569067b1972e9dee013537c71a5b073168f9"}, + "credo": {:hex, :credo, "1.7.19", "cc52129665fc7c15143d47838fda0f9cd6dac9ceced7bf4da6f85fcbfe64b12a", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2d8bc95d5a7bb99dd2613621d4f08c6a3575c3fd4b62e6a2b48a100352a557b8"}, + "decimal": {:hex, :decimal, "3.1.1", "430d87b04011ce6cbd4fd205be758311a81f87d552d40904abd00f015935b1d0", [:mix], [], "hexpm", "c5f25f2ced74a0587d03e6023f595db8e924c9d3922c8c8ffd9edfc4498cf1f6"}, "dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"}, "diskusage_logger": {:hex, :diskusage_logger, "0.2.0", "04fc48b538fe4de43153542a71ea94f623d54707d85844123baacfceedf625c3", [:mix], [], "hexpm", "e3f2aed1b0fc4590931c089a6453a4c4eb4c945912aa97bcabcc0cff7851f34d"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, From 63c74d7b6b9eae5bb7d1c1ea353a9c98aa247ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jadrian=20Sardi=C3=B1as?= Date: Tue, 9 Jun 2026 16:26:43 -0400 Subject: [PATCH 3/3] Fix `Credo.Check.Refactor.Nesting` violations. --- apps/api_web/lib/api_web/api_view_helpers.ex | 25 +++---- .../controllers/live_facility_controller.ex | 28 ++++---- .../controllers/prediction_controller.ex | 71 ++++++++++--------- apps/state/lib/state/route.ex | 12 ++-- 4 files changed, 74 insertions(+), 62 deletions(-) diff --git a/apps/api_web/lib/api_web/api_view_helpers.ex b/apps/api_web/lib/api_web/api_view_helpers.ex index 16edfe525..708e12b33 100644 --- a/apps/api_web/lib/api_web/api_view_helpers.ex +++ b/apps/api_web/lib/api_web/api_view_helpers.ex @@ -126,22 +126,23 @@ defmodule ApiWeb.ApiViewHelpers do for item <- data do child_id = Map.get(item, id_key) - - child = - if child = Map.get(bulk_children, child_id) do - child - else - if is_nil(fallback_fn) do - nil - else - fallback_fn.(child_id) - end - end - + child = maybe_get_child(bulk_children, child_id, fallback_fn) Map.put(item, key, child) end end + defp maybe_get_child(bulk_children, child_id, fallback_fn) do + if child = Map.get(bulk_children, child_id) do + child + else + if is_nil(fallback_fn) do + nil + else + fallback_fn.(child_id) + end + end + end + defp preload_trip_fallback(trip_id) do predictions = State.Prediction.by_trip_id(trip_id) diff --git a/apps/api_web/lib/api_web/controllers/live_facility_controller.ex b/apps/api_web/lib/api_web/controllers/live_facility_controller.ex index 3fda65a52..7a82c0d88 100644 --- a/apps/api_web/lib/api_web/controllers/live_facility_controller.ex +++ b/apps/api_web/lib/api_web/controllers/live_facility_controller.ex @@ -55,18 +55,7 @@ defmodule ApiWeb.LiveFacilityController do {:ok, filtered} <- Params.filter_params(params, @filters, conn) do case filtered do %{"id" => ids} -> - ids - |> split_on_comma - |> State.Facility.Parking.by_facility_ids() - |> Enum.group_by(& &1.facility_id) - |> Enum.map(fn {facilty_id, properties} -> - %{ - facility_id: facilty_id, - properties: properties, - updated_at: updated_at(properties) - } - end) - |> State.all(Params.filter_opts(params, @pagination_opts, conn)) + do_index_data(ids, conn, params) _ -> {:error, :filter_required} @@ -76,6 +65,21 @@ defmodule ApiWeb.LiveFacilityController do end end + defp do_index_data(ids, conn, params) do + ids + |> split_on_comma + |> State.Facility.Parking.by_facility_ids() + |> Enum.group_by(& &1.facility_id) + |> Enum.map(fn {facilty_id, properties} -> + %{ + facility_id: facilty_id, + properties: properties, + updated_at: updated_at(properties) + } + end) + |> State.all(Params.filter_opts(params, @pagination_opts, conn)) + end + swagger_path :show do get(path("live_facility", :show)) diff --git a/apps/api_web/lib/api_web/controllers/prediction_controller.ex b/apps/api_web/lib/api_web/controllers/prediction_controller.ex index 8ecb619c3..385711c44 100644 --- a/apps/api_web/lib/api_web/controllers/prediction_controller.ex +++ b/apps/api_web/lib/api_web/controllers/prediction_controller.ex @@ -78,45 +78,12 @@ defmodule ApiWeb.PredictionController do def index_data(conn, params) do with :ok <- Params.validate_includes(params, @includes, conn), {:ok, filtered_params} <- Params.filter_params(params, filters(conn), conn) do - pagination_opts = - Params.filter_opts(params, @pagination_opts, conn, order_by: {:arrival_time, :asc}) - - stop_ids = stop_ids(filtered_params, conn) - route_ids = Params.split_on_comma(filtered_params, "route") - route_pattern_ids = Params.split_on_comma(filtered_params, "route_pattern") - route_types = Params.route_types(filtered_params) - revenue = filtered_params |> Map.get("revenue") |> Params.revenue() - - direction_id_matcher = - filtered_params - |> Params.direction_id() - |> direction_id_matcher() - - matchers = - filtered_params - |> build_stop_sequence_matchers(direction_id_matcher) - |> add_revenue_matchers(revenue) - case filtered_params do %{"route_type" => _} = p when map_size(p) == 1 -> {:error, :only_route_type} p when map_size(p) > 0 -> - trip_ids = Params.split_on_comma(filtered_params, "trip") - - {trip_ids, route_pattern_ids} - |> case do - {[], []} -> - all_stops_and_routes(stop_ids, route_ids, matchers) - - {[], route_pattern_ids} -> - all_stops_and_route_patterns(stop_ids, route_pattern_ids, matchers) - - {trip_ids, _} -> - all_stops_and_trips(stop_ids, trip_ids, matchers) - end - |> Prediction.filter_by_route_type(route_types) - |> State.all(pagination_opts) + do_index_data(conn, params, filtered_params) _ -> {:error, :filter_required} @@ -126,6 +93,42 @@ defmodule ApiWeb.PredictionController do end end + defp do_index_data(conn, params, filtered_params) do + revenue = filtered_params |> Map.get("revenue") |> Params.revenue() + trip_ids = Params.split_on_comma(filtered_params, "trip") + route_pattern_ids = Params.split_on_comma(filtered_params, "route_pattern") + stop_ids = stop_ids(filtered_params, conn) + route_ids = Params.split_on_comma(filtered_params, "route") + route_types = Params.route_types(filtered_params) + + pagination_opts = + Params.filter_opts(params, @pagination_opts, conn, order_by: {:arrival_time, :asc}) + + direction_id_matcher = + filtered_params + |> Params.direction_id() + |> direction_id_matcher() + + matchers = + filtered_params + |> build_stop_sequence_matchers(direction_id_matcher) + |> add_revenue_matchers(revenue) + + {trip_ids, route_pattern_ids} + |> case do + {[], []} -> + all_stops_and_routes(stop_ids, route_ids, matchers) + + {[], route_pattern_ids} -> + all_stops_and_route_patterns(stop_ids, route_pattern_ids, matchers) + + {trip_ids, _} -> + all_stops_and_trips(stop_ids, trip_ids, matchers) + end + |> Prediction.filter_by_route_type(route_types) + |> State.all(pagination_opts) + end + defp filters(%{assigns: %{api_version: ver}}) when ver < "2021-01-09", do: ["date" | @filters] defp filters(_), do: @filters diff --git a/apps/state/lib/state/route.ex b/apps/state/lib/state/route.ex index 4b69b1130..6736ca83f 100644 --- a/apps/state/lib/state/route.ex +++ b/apps/state/lib/state/route.ex @@ -60,10 +60,7 @@ defmodule State.Route do for direction_id <- ["0", "1"] do case Map.get(map, {route_id, direction_id}, nil) do [d | _tail] -> - case Map.fetch!(d, field) do - "" -> nil - value -> value - end + fetch_nil_if_empty(d, field) _ -> nil @@ -71,6 +68,13 @@ defmodule State.Route do end end + defp fetch_nil_if_empty(map, field) do + case Map.fetch!(map, field) do + "" -> nil + value -> value + end + end + def hidden?(%{listed_route: false}), do: true def hidden?(%{agency_id: agency_id}) when agency_id != "1", do: true def hidden?(_), do: false