From 4866bfc8d42dbb285efc7904f923854a8428111e Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Fri, 19 Apr 2024 12:31:25 -0500 Subject: [PATCH 01/19] swap req in for httpoison --- config/config.exs | 4 +- config/dotcom/v3api.exs | 10 -- config/runtime.exs | 8 +- config/test.exs | 10 +- lib/json_api.ex | 23 ++-- lib/mbta/api.ex | 154 +++-------------------- lib/mbta/api/alerts.ex | 2 +- lib/mbta/api/behaviour.ex | 7 +- lib/mbta/api/facilities.ex | 10 +- lib/mbta/api/predictions.ex | 2 +- lib/mbta/api/route_patterns.ex | 2 +- lib/mbta/api/routes.ex | 2 +- lib/mbta/api/schedules.ex | 2 +- lib/mbta/api/services.ex | 2 +- lib/mbta/api/shapes.ex | 6 +- lib/mbta/api/stops.ex | 10 +- lib/mbta/api/stream.ex | 27 ++-- lib/mbta/api/trips.ex | 12 +- lib/mbta/headers.ex | 35 ------ lib/mbta/sentry_extra.ex | 47 ------- mix.exs | 1 + mix.lock | 7 +- test/mbta/api_test.exs | 87 +++---------- test/mbta/headers_test.exs | 48 ------- test/mbta/sentry_extra_test.exs | 68 ---------- test/stops/api_test.exs | 10 +- test/support/behaviours/req/behaviour.ex | 11 ++ test/support/mocks.ex | 1 + 28 files changed, 122 insertions(+), 486 deletions(-) delete mode 100644 config/dotcom/v3api.exs delete mode 100644 lib/mbta/headers.ex delete mode 100644 lib/mbta/sentry_extra.ex delete mode 100644 test/mbta/headers_test.exs delete mode 100644 test/mbta/sentry_extra_test.exs create mode 100644 test/support/behaviours/req/behaviour.ex diff --git a/config/config.exs b/config/config.exs index a2571f30a6..5a98a28c1f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -4,11 +4,13 @@ config :elixir, ansi_enabled: true config :dotcom, :httpoison, HTTPoison +config :dotcom, :mbta_api_module, MBTA.Api + config :dotcom, :redis, Dotcom.Cache.Multilevel.Redis config :dotcom, :redix, Redix config :dotcom, :redix_pub_sub, Redix.PubSub -config :dotcom, :mbta_api, MBTA.Api +config :dotcom, :req_module, Req for config_file <- Path.wildcard("config/{deps,dotcom}/*.exs") do import_config("../#{config_file}") diff --git a/config/dotcom/v3api.exs b/config/dotcom/v3api.exs deleted file mode 100644 index 2027860952..0000000000 --- a/config/dotcom/v3api.exs +++ /dev/null @@ -1,10 +0,0 @@ -import Config - -# Allow more time for API requests on CI and prod -default_timeout = if config_env() == :dev, do: 5_000, else: 10_000 -cache_size = if config_env() == :prod, do: 200_000, else: 10_000 - -config :dotcom, - v3_api_default_timeout: default_timeout, - v3_api_cache_size: cache_size, - v3_api_http_pool: :v3_api_http_pool diff --git a/config/runtime.exs b/config/runtime.exs index 0a1722aec5..4ea4c1e387 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -107,10 +107,10 @@ else ] end -config :dotcom, - v3_api_base_url: System.get_env("V3_URL"), - v3_api_key: System.get_env("V3_API_KEY"), - v3_api_version: System.get_env("V3_API_VERSION", "2019-07-01") +config :dotcom, :mbta_api, + base_url: System.get_env("MBTA_API_BASE_URL"), + key: System.get_env("MBTA_API_KEY"), + version: System.get_env("MBTA_API_VERSION", "2019-07-01") config :dotcom, aws_index_prefix: System.get_env("AWS_PLACE_INDEX_PREFIX") || "dotcom-dev" diff --git a/config/test.exs b/config/test.exs index 98b87e1180..b4453b13e8 100644 --- a/config/test.exs +++ b/config/test.exs @@ -1,11 +1,15 @@ import Config config :dotcom, :cache, Dotcom.Cache.TestCache + +config :dotcom, :httpoison, HTTPoison.Mock + +config :dotcom, :mbta_api_module, MBTA.Api.Mock + config :dotcom, :redis, Dotcom.Redis.Mock config :dotcom, :redix, Dotcom.Redix.Mock config :dotcom, :redix_pub_sub, Dotcom.Redix.PubSub.Mock -config :dotcom, :trip_plan_feedback_cache, Dotcom.Cache.TestCache -config :dotcom, :httpoison, HTTPoison.Mock +config :dotcom, :req_module, Req.Mock -config :dotcom, :mbta_api, MBTA.Api.Mock +config :dotcom, :trip_plan_feedback_cache, Dotcom.Cache.TestCache diff --git a/lib/json_api.ex b/lib/json_api.ex index b720b49e6b..3e0d50ce21 100644 --- a/lib/json_api.ex +++ b/lib/json_api.ex @@ -40,15 +40,22 @@ defmodule JsonApi do } end - @spec parse(String.t()) :: JsonApi.t() | {:error, any} + @spec parse(String.t() | map()) :: {:error, any()} | JsonApi.t() + def parse(body) when is_binary(body) do + case Jason.decode(body) do + {:ok, parsed} -> parse(parsed) + {:error, error} -> {:error, error} + end + end + def parse(body) do - with {:ok, parsed} <- Jason.decode(body), - {:ok, data} <- parse_data(parsed) do - %JsonApi{ - links: parse_links(parsed), - data: data - } - else + case parse_data(body) do + {:ok, data} -> + %JsonApi{ + links: parse_links(body), + data: data + } + {:error, [_ | _] = errors} -> {:error, parse_errors(errors)} diff --git a/lib/mbta/api.ex b/lib/mbta/api.ex index 18624c3f6a..5ad1ff19ad 100644 --- a/lib/mbta/api.ex +++ b/lib/mbta/api.ex @@ -3,152 +3,28 @@ defmodule MBTA.Api do Handles fetching and caching generic JSON:API responses from the MBTA API. """ - require Logger - - use HTTPoison.Base - - alias MBTA.SentryExtra - alias Util - @behaviour MBTA.Api.Behaviour - @default_timeout Application.compile_env!(:dotcom, :v3_api_default_timeout) - @httpoison Application.compile_env!(:dotcom, :httpoison) - @http_pool Application.compile_env!(:dotcom, :v3_api_http_pool) + @req Application.compile_env(:dotcom, :req_module) @impl MBTA.Api.Behaviour - def get_json(url, params \\ [], opts \\ []) do - _ = - Logger.debug(fn -> - "MBTA.Api.get_json url=#{url} params=#{params |> Map.new() |> Poison.encode!()}" - end) - - body = "" - opts = Keyword.merge(default_options(), opts) - - with {time, response} <- timed_get(url, params, opts), - :ok <- log_response(url, params, time, response), - {:ok, http_response} <- response, - {:ok, body} <- body(http_response) do - body - |> JsonApi.parse() - |> maybe_log_parse_error(url, params, body) - else - {:error, error} -> - _ = log_response_error(url, params, body) - {:error, error} - - error -> - _ = log_response_error(url, params, body) - {:error, error} + def get_json(url, params \\ []) do + case client() |> @req.get(url: url, params: params) do + {:ok, response} -> JsonApi.parse(response.body) + {:error, reason} -> {:error, reason} end end - defp timed_get(url, params, opts) do - api_key = Keyword.fetch!(opts, :api_key) - base_url = Keyword.fetch!(opts, :base_url) - - headers = MBTA.Headers.build(api_key) - - url = base_url <> URI.encode(url) - timeout = Keyword.fetch!(opts, :timeout) - - {time, response} = - :timer.tc(fn -> - @httpoison.get(url, headers, - params: params, - timeout: timeout, - recv_timeout: timeout, - hackney: [pool: @http_pool] - ) - end) - - {time, response} - end - - @spec maybe_log_parse_error(JsonApi.t() | {:error, any}, String.t(), Keyword.t(), String.t()) :: - JsonApi.t() | {:error, any} - defp maybe_log_parse_error({:error, error}, url, params, body) do - _ = log_response_error(url, params, body) - {:error, error} - end - - defp maybe_log_parse_error(response, _, _, _) do - response - end - - @spec log_response(String.t(), Keyword.t(), integer, any) :: :ok - defp log_response(url, params, time, response) do - entry = fn -> - "MBTA.Api.get_json_response url=#{inspect(url)} " <> - "params=#{params |> Map.new() |> Poison.encode!()} " <> - log_body(response) <> - " duration=#{time / 1000}" <> - " request_id=#{Logger.metadata() |> Keyword.get(:request_id)}" - end - - _ = SentryExtra.log_context("api-response", entry) - _ = Logger.info(entry) - :ok - end - - @spec log_response_error(String.t(), Keyword.t(), String.t()) :: :ok - defp log_response_error(url, params, body) do - entry = fn -> - "MBTA.Api.get_json_response url=#{inspect(url)} " <> - "params=#{params |> Map.new() |> Poison.encode!()} response=" <> body - end - - _ = SentryExtra.log_context("api-response-error", entry) - _ = Logger.info(entry) - :ok - end - - defp log_body({:ok, response}) do - "status=#{response.status_code} content_length=#{byte_size(response.body)}" - end - - defp log_body({:error, error}) do - ~s(status=error error="#{inspect(error)}") - end - - def body(%{headers: headers, body: body}) do - case Enum.find( - headers, - &(String.downcase(elem(&1, 0)) == "content-encoding") - ) do - {_, "gzip"} -> - {:ok, :zlib.gunzip(body)} - - _ -> - {:ok, body} - end - rescue - e in ErlangError -> {:error, e.original} - end - - def body(other) do - other - end - - @impl HTTPoison.Base - def process_request_headers(headers) do - [ - {"accept-encoding", "gzip"}, - {"accept", "application/vnd.api+json"} - | headers - ] - end - - defp default_options do - [ - base_url: config(:v3_api_base_url), - api_key: config(:v3_api_key), - timeout: @default_timeout - ] - end + defp client do + config = Application.get_env(:dotcom, :mbta_api) - defp config(key) do - Util.config(:dotcom, key) + @req.new( + base_url: config[:base_url], + headers: [ + {"MBTA-Version", config[:version]}, + {"x-api-key", config[:key]}, + {"x-enable-experimental-features", "true"} + ] + ) end end diff --git a/lib/mbta/api/alerts.ex b/lib/mbta/api/alerts.ex index efe9fef70e..ee2d22bfc4 100644 --- a/lib/mbta/api/alerts.ex +++ b/lib/mbta/api/alerts.ex @@ -3,7 +3,7 @@ defmodule MBTA.Api.Alerts do Responsible for fetching Alert data from the V3 API. """ - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) @spec all() :: JsonApi.t() | {:error, any} def all(params \\ []) do diff --git a/lib/mbta/api/behaviour.ex b/lib/mbta/api/behaviour.ex index 63f906c2f1..1d3db7e7d9 100644 --- a/lib/mbta/api/behaviour.ex +++ b/lib/mbta/api/behaviour.ex @@ -5,9 +5,8 @@ defmodule MBTA.Api.Behaviour do @callback get_json(String.t()) :: JsonApi.t() | {:error, any} @callback get_json(String.t(), Keyword.t()) :: JsonApi.t() | {:error, any} - @callback get_json(String.t(), Keyword.t(), Keyword.t()) :: JsonApi.t() | {:error, any} - @implementation Application.compile_env!(:dotcom, :mbta_api) + @implementation Application.compile_env!(:dotcom, :mbta_api_module) def get_json(url) do @implementation.get_json(url) @@ -16,8 +15,4 @@ defmodule MBTA.Api.Behaviour do def get_json(url, params) do @implementation.get_json(url, params) end - - def get_json(url, params, opts) do - @implementation.get_json(url, params, opts) - end end diff --git a/lib/mbta/api/facilities.ex b/lib/mbta/api/facilities.ex index 091c50f649..8bf8a37193 100644 --- a/lib/mbta/api/facilities.ex +++ b/lib/mbta/api/facilities.ex @@ -3,15 +3,15 @@ defmodule MBTA.Api.Facilities do Responsible for fetching Stop data from the V3 API. """ - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) - def all(params \\ [], opts \\ []) do - @mbta_api.get_json("/facilities/", params, opts) + def all(params \\ []) do + @mbta_api.get_json("/facilities/", params) end - def filter_by(filters, opts \\ []) do + def filter_by(filters) do params = Enum.map(filters, fn {k, v} -> {"filter[#{k}]", v} end) - @mbta_api.get_json("/facilities/", params, opts) + @mbta_api.get_json("/facilities/", params) end end diff --git a/lib/mbta/api/predictions.ex b/lib/mbta/api/predictions.ex index 3b2f7636a9..df474da508 100644 --- a/lib/mbta/api/predictions.ex +++ b/lib/mbta/api/predictions.ex @@ -3,7 +3,7 @@ defmodule MBTA.Api.Predictions do Responsible for fetching Prediction data from the V3 API. """ - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) def all(params) do @mbta_api.get_json("/predictions/", params) diff --git a/lib/mbta/api/route_patterns.ex b/lib/mbta/api/route_patterns.ex index ec32034904..dee81c0460 100644 --- a/lib/mbta/api/route_patterns.ex +++ b/lib/mbta/api/route_patterns.ex @@ -5,7 +5,7 @@ defmodule MBTA.Api.RoutePatterns do alias Routes.Route - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) @type api_response_t() :: JsonApi.t() | {:error, any} diff --git a/lib/mbta/api/routes.ex b/lib/mbta/api/routes.ex index ff59704a4f..ac273d4a4a 100644 --- a/lib/mbta/api/routes.ex +++ b/lib/mbta/api/routes.ex @@ -6,7 +6,7 @@ defmodule MBTA.Api.Routes do alias Routes.Route alias Stops.Stop - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) @type api_response_t() :: JsonApi.t() | {:error, any} diff --git a/lib/mbta/api/schedules.ex b/lib/mbta/api/schedules.ex index 7e65e2d746..891c570150 100644 --- a/lib/mbta/api/schedules.ex +++ b/lib/mbta/api/schedules.ex @@ -3,7 +3,7 @@ defmodule MBTA.Api.Schedules do Responsible for fetching Schedule data from the V3 API. """ - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) def all(params \\ []) do @mbta_api.get_json("/schedules/", params) diff --git a/lib/mbta/api/services.ex b/lib/mbta/api/services.ex index f11ea160b6..9a6f459a87 100644 --- a/lib/mbta/api/services.ex +++ b/lib/mbta/api/services.ex @@ -1,7 +1,7 @@ defmodule MBTA.Api.Services do @moduledoc false - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) def all(params \\ []) do @mbta_api.get_json("/services/", params) diff --git a/lib/mbta/api/shapes.ex b/lib/mbta/api/shapes.ex index 3416cf4ba3..6cebe7b643 100644 --- a/lib/mbta/api/shapes.ex +++ b/lib/mbta/api/shapes.ex @@ -3,13 +3,13 @@ defmodule MBTA.Api.Shapes do Responsible for fetching Shape data from the V3 API. """ - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) def all(params \\ []) do @mbta_api.get_json("/shapes/", params) end - def by_id(id, opts \\ []) do - @mbta_api.get_json("/shapes/" <> id, [], opts) + def by_id(id) do + @mbta_api.get_json("/shapes/" <> id) end end diff --git a/lib/mbta/api/stops.ex b/lib/mbta/api/stops.ex index 3b45156c9d..dd88d72f4a 100644 --- a/lib/mbta/api/stops.ex +++ b/lib/mbta/api/stops.ex @@ -3,17 +3,13 @@ defmodule MBTA.Api.Stops do Responsible for fetching Stop data from the V3 API. """ - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) def all(params \\ []) do @mbta_api.get_json("/stops/", params) end - def by_gtfs_id(gtfs_id, params, opts \\ []) do - @mbta_api.get_json( - "/stops/#{gtfs_id}", - params, - opts - ) + def by_gtfs_id(gtfs_id, params \\ []) do + @mbta_api.get_json("/stops/#{gtfs_id}", params) end end diff --git a/lib/mbta/api/stream.ex b/lib/mbta/api/stream.ex index 5236548bcd..9f402a609f 100644 --- a/lib/mbta/api/stream.ex +++ b/lib/mbta/api/stream.ex @@ -15,12 +15,11 @@ defmodule MBTA.Api.Stream do Other options are made available for tests, and can include: - :name (name of the GenStage process) - :base_url - - :api_key + - :key """ use GenStage - alias MBTA.Headers alias ServerSentEventStage, as: SSES defmodule Event do @@ -67,20 +66,23 @@ defmodule MBTA.Api.Stream do @spec default_options :: Keyword.t() defp default_options do - with base_url when not is_nil(base_url) <- config(:v3_api_base_url), - api_key when not is_nil(api_key) <- config(:v3_api_key) do + with base_url when not is_nil(base_url) <- config(:base_url), + key when not is_nil(key) <- config(:key) do [ base_url: base_url, - api_key: api_key + key: key ] else _ -> - raise ArgumentError, "Missing valid V3_URL and/or V3_API_KEY" + raise ArgumentError, "Missing required configuration for MBTA API" end end @spec config(atom) :: any - defp config(key), do: Util.config(:dotcom, key) + defp config(key) do + config = Application.get_env(:dotcom, :mbta_api) + config[key] + end @spec set_url(Keyword.t()) :: Keyword.t() defp set_url(opts) do @@ -97,10 +99,13 @@ defmodule MBTA.Api.Stream do @spec set_headers(Keyword.t()) :: Keyword.t() defp set_headers(opts) do - headers = - opts - |> Keyword.fetch!(:api_key) - |> Headers.build() + config = Application.get_env(:dotcom, :mbta_api) + + headers = [ + {"MBTA-Version", config[:version]}, + {"x-api-key", config[:key]}, + {"x-enable-experimental-features", "true"} + ] Keyword.put(opts, :headers, headers) end diff --git a/lib/mbta/api/trips.ex b/lib/mbta/api/trips.ex index 3df58f7519..ed050d8a00 100644 --- a/lib/mbta/api/trips.ex +++ b/lib/mbta/api/trips.ex @@ -3,15 +3,15 @@ defmodule MBTA.Api.Trips do Responsible for fetching Trip data from the MBTA Api. """ - @mbta_api Application.compile_env!(:dotcom, :mbta_api) + @mbta_api Application.compile_env!(:dotcom, :mbta_api_module) - def by_id(id, opts \\ []) do - @mbta_api.get_json("/trips/" <> id, opts) + def by_id(id, params \\ []) do + @mbta_api.get_json("/trips/" <> id, params) end - def by_route(route_id, opts \\ []) do - opts = Kernel.put_in(opts[:route], route_id) + def by_route(route_id, params \\ []) do + params = Kernel.put_in(params[:route], route_id) - @mbta_api.get_json("/trips/", opts) + @mbta_api.get_json("/trips/", params) end end diff --git a/lib/mbta/headers.ex b/lib/mbta/headers.ex deleted file mode 100644 index 15cc7fa740..0000000000 --- a/lib/mbta/headers.ex +++ /dev/null @@ -1,35 +0,0 @@ -defmodule MBTA.Headers do - @moduledoc """ - Builds headers for calling the MBTA.Api. - """ - - @type header_list :: [{String.t(), String.t()}] - - @spec build(String.t()) :: header_list - def build(api_key) do - [] - |> api_key_header(api_key) - |> extra_headers() - end - - @spec api_key_header(header_list, String.t() | nil) :: header_list - defp api_key_header(headers, nil), do: headers - - defp api_key_header(headers, <>) do - api_version = Util.config(:dotcom, :v3_api_version) - [{"x-api-key", key}, {"MBTA-Version", api_version} | headers] - end - - @spec extra_headers(header_list) :: header_list - defp extra_headers(headers) do - Util.config(:dotcom, :enable_experimental_features) - |> do_extra_headers(headers) - end - - @spec do_extra_headers(boolean() | nil, header_list) :: header_list - defp do_extra_headers("true", headers) do - [{"x-enable-experimental-features", "true"} | headers] - end - - defp do_extra_headers(_, headers), do: headers -end diff --git a/lib/mbta/sentry_extra.ex b/lib/mbta/sentry_extra.ex deleted file mode 100644 index 08573cfa4c..0000000000 --- a/lib/mbta/sentry_extra.ex +++ /dev/null @@ -1,47 +0,0 @@ -defmodule MBTA.SentryExtra do - @moduledoc """ - log up to 50 messages of "extra context" when using the Sentry error message log service - """ - - @process_dictionary_count :sentry_count - @process_dictionary_max 50 - - @spec log_context(String.t(), String.t() | fun) :: nil - def log_context(entry_type, data_callback) when is_function(data_callback) do - do_log_context(entry_type, data_callback.()) - end - - def log_context(entry_type, data) do - do_log_context(entry_type, data) - end - - @spec do_log_context(String.t(), String.t()) :: nil - defp do_log_context(entry_type, data) do - count = set_dictionary_count(get_dictionary_count()) - Sentry.Context.set_extra_context(%{"#{entry_type}-#{count}" => data}) - end - - @spec get_dictionary_count :: integer - defp get_dictionary_count do - Process.get(@process_dictionary_count, 0) - end - - @spec set_dictionary_count(integer) :: integer - defp set_dictionary_count(count) do - next_count = - if count >= @process_dictionary_max do - purge_dictionary() - else - count + 1 - end - - Process.put(@process_dictionary_count, next_count) - next_count - end - - @spec purge_dictionary :: integer - defp purge_dictionary do - Sentry.Context.clear_all() - 1 - end -end diff --git a/mix.exs b/mix.exs index 05ae893e76..7260a1901f 100644 --- a/mix.exs +++ b/mix.exs @@ -134,6 +134,7 @@ defmodule DotCom.Mixfile do {:recase, "0.7.0"}, {:recon, "2.5.5", [only: :prod]}, {:redix, "1.4.1"}, + {:req, "0.4.14", override: true}, {:rstar, github: "armon/erl-rstar"}, # latest version 10.1.0; cannot upgrade because setup appears to have changed {:sentry, "7.2.5"}, diff --git a/mix.lock b/mix.lock index 26945540dc..dbc52b2bc0 100644 --- a/mix.lock +++ b/mix.lock @@ -32,7 +32,7 @@ "expo": {:hex, :expo, "0.5.1", "249e826a897cac48f591deba863b26c16682b43711dd15ee86b92f25eafd96d9", [:mix], [], "hexpm", "68a4233b0658a3d12ee00d27d37d856b1ba48607e7ce20fd376958d0ba6ce92b"}, "faker": {:hex, :faker, "0.18.0", "943e479319a22ea4e8e39e8e076b81c02827d9302f3d32726c5bf82f430e6e14", [:mix], [], "hexpm", "bfbdd83958d78e2788e99ec9317c4816e651ad05e24cfd1196ce5db5b3e81797"}, "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, - "finch": {:hex, :finch, "0.17.0", "17d06e1d44d891d20dbd437335eebe844e2426a0cd7e3a3e220b461127c73f70", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8d014a661bb6a437263d4b5abf0bcbd3cf0deb26b1e8596f2a271d22e48934c7"}, + "finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"}, "floki": {:hex, :floki, "0.36.1", "712b7f2ba19a4d5a47dfe3e74d81876c95bbcbee44fe551f0af3d2a388abb3da", [:mix], [], "hexpm", "21ba57abb8204bcc70c439b423fc0dd9f0286de67dc82773a14b0200ada0995f"}, "gen_stage": {:hex, :gen_stage, "1.2.1", "19d8b5e9a5996d813b8245338a28246307fd8b9c99d1237de199d21efc4c76a1", [:mix], [], "hexpm", "83e8be657fa05b992ffa6ac1e3af6d57aa50aace8f691fcf696ff02f8335b001"}, "gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"}, @@ -62,8 +62,9 @@ "nebulex": {:hex, :nebulex, "2.6.1", "58c1924fa9f4e844c3470c20e6351b311a556652de29ed3b05fd2e5d817c6fef", [:mix], [{:decorator, "~> 1.4", [hex: :decorator, repo: "hexpm", optional: true]}, {:shards, "~> 1.1", [hex: :shards, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "177949fef2dc34a0055d7140b6bc94f6904225e2b5bbed6266ea9679522d23c6"}, "nebulex_redis_adapter": {:hex, :nebulex_redis_adapter, "2.4.0", "bebd7aac9fc92378115131b9d90f094c62e5d72869d97191550fb0591d6c1d8a", [:mix], [{:crc, "~> 0.10", [hex: :crc, repo: "hexpm", optional: true]}, {:jchash, "~> 0.1", [hex: :jchash, repo: "hexpm", optional: true]}, {:nebulex, "~> 2.6", [hex: :nebulex, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.5 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:redix, "~> 1.3", [hex: :redix, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "0934142f92b71519fb81324721fab7bc164036c5b91eef7e879a63b98e659814"}, "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, + "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"}, + "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "open_trip_planner_client": {:git, "https://github.com/thecristen/open_trip_planner_client.git", "db72c188f45e2b332168001614548df90cb6814a", [ref: "v0.6.3"]}, "parallel_stream": {:hex, :parallel_stream, "1.1.0", "f52f73eb344bc22de335992377413138405796e0d0ad99d995d9977ac29f1ca9", [:mix], [], "hexpm", "684fd19191aedfaf387bbabbeb8ff3c752f0220c8112eb907d797f4592d6e871"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, @@ -89,7 +90,7 @@ "recase": {:hex, :recase, "0.7.0", "3f2f719f0886c7a3b7fe469058ec539cb7bbe0023604ae3bce920e186305e5ae", [:mix], [], "hexpm", "36f5756a9f552f4a94b54a695870e32f4e72d5fad9c25e61bc4a3151c08a4e0c"}, "recon": {:hex, :recon, "2.5.5", "c108a4c406fa301a529151a3bb53158cadc4064ec0c5f99b03ddb8c0e4281bdf", [:mix, :rebar3], [], "hexpm", "632a6f447df7ccc1a4a10bdcfce71514412b16660fe59deca0fcf0aa3c054404"}, "redix": {:hex, :redix, "1.4.1", "8303e13bad38ca80c15bdf79ea9cbd6eb879554c9cbb815b35df1602d7b1549d", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:nimble_options, "~> 0.5.0 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "676b5ce37d7b1d46931d506e3208786bd8334a1625ecb591d87d790b23ffbd1f"}, - "req": {:hex, :req, "0.3.12", "f84c2f9e7cc71c81d7cbeacf7c61e763e53ab5f3065703792a4ab264b4f22672", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.9", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "c91103d4d1c8edeba90c84e0ba223a59865b673eaab217bfd17da3aa54ab136c"}, + "req": {:hex, :req, "0.4.14", "103de133a076a31044e5458e0f850d5681eef23dfabf3ea34af63212e3b902e2", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0 or ~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "2ddd3d33f9ab714ced8d3c15fd03db40c14dbf129003c4a3eb80fac2cc0b1b08"}, "rstar": {:git, "https://github.com/armon/erl-rstar.git", "a406b2cce609029bf65b9ccfbe93a0416c0ee0cd", []}, "sentry": {:hex, :sentry, "7.2.5", "570db92c3bbacd6ad02ac81cba8ac5af11235a55d65ac4375e3ec833975b83d3", [:mix], [{:hackney, "1.6.5 or ~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "ea84ed6848505ff2a246567df562f465d2b34c317d3ecba7c7df58daa56e5e5d"}, "server_sent_event_stage": {:hex, :server_sent_event_stage, "1.1.0", "cd5f93e1110455be569533b3c68503fce9f33bbd6e8895f5d5bc139b47d79cab", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:ex_doc, "~> 0.21", [hex: :ex_doc, repo: "hexpm", optional: true]}, {:gen_stage, "~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:mint, "~> 1.4", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "5a5fdcd34cac962a24bcbffef843c5bb1127f0cf8f84795dcdf3c8949051ed6f"}, diff --git a/test/mbta/api_test.exs b/test/mbta/api_test.exs index 690618734a..40c28e2e9c 100644 --- a/test/mbta/api_test.exs +++ b/test/mbta/api_test.exs @@ -10,25 +10,15 @@ defmodule MBTA.ApiTest do describe "get_json/1" do test "normal responses return a JsonApi struct" do - expect(HTTPoison.Mock, :get, fn _, _, _ -> - {:ok, %HTTPoison.Response{status_code: 200, body: ~s({"data": []})}} + expect(Req.Mock, :new, fn _ -> + %Req.Request{} end) - response = Api.get_json("/normal_response") - - assert %JsonApi{} = response - - refute response.data == %{} - end - - test "encodes the URL" do - expect(HTTPoison.Mock, :get, fn url, _, _ -> - assert url =~ "normal%20response" - + expect(Req.Mock, :get, fn _, _ -> {:ok, %HTTPoison.Response{status_code: 200, body: ~s({"data": []})}} end) - response = Api.get_json("/normal response") + response = Api.get_json("/normal_response") assert %JsonApi{} = response @@ -36,8 +26,12 @@ defmodule MBTA.ApiTest do end test "missing endpoints return an error" do - expect(HTTPoison.Mock, :get, fn _, _, _ -> - {:ok, %HTTPoison.Response{status_code: 404, body: ~s({"errors":[{"code": "not_found"}]})}} + expect(Req.Mock, :new, fn _ -> + %Req.Request{} + end) + + expect(Req.Mock, :get, fn _, _ -> + {:ok, %Req.Response{status: 404, body: ~s({"errors":[{"code": "not_found"}]})}} end) response = Api.get_json("/missing") @@ -46,66 +40,17 @@ defmodule MBTA.ApiTest do end test "can't connect returns an error" do - expect(HTTPoison.Mock, :get, fn _, _, _ -> - {:error, %HTTPoison.Error{reason: :econnrefused}} - end) - - response = Api.get_json("/cant_connect") - - assert {:error, %{reason: _}} = response - end - - test "passes an API key if present" do - api_key = Faker.UUID.v4() - - expect(HTTPoison.Mock, :get, fn _, headers, _ -> - assert Enum.any?(headers, fn {k, v} -> k == "x-api-key" && v == api_key end) - - {:ok, %HTTPoison.Response{status_code: 200, body: ~s({"data": []})}} + expect(Req.Mock, :new, fn _ -> + %Req.Request{} end) - Api.get_json("/with_api_key", [], api_key: api_key) - end - - test "does not pass an API key if not set" do - expect(HTTPoison.Mock, :get, fn _, headers, _ -> - refute Enum.any?(headers, fn {k, _} -> k == "x-api-key" end) - - {:ok, %HTTPoison.Response{status_code: 200, body: ~s({"data": []})}} + expect(Req.Mock, :get, fn _, _ -> + {:error, %{reason: :econnrefused}} end) - Api.get_json("/without_api_key", [], api_key: nil) - end - end - - describe "body/1" do - test "returns a normal body if there's no content-encoding" do - response = %HTTPoison.Response{headers: [], body: "body"} - - assert Api.body(response) == {:ok, "body"} - end - - test "decodes a gzip encoded body" do - body = "body" - encoded_body = :zlib.gzip(body) - header = {"Content-Encoding", "gzip"} - response = %HTTPoison.Response{headers: [header], body: encoded_body} - - assert {:ok, ^body} = Api.body(response) - end - - test "returns an error if the gzip body is invalid" do - encoded_body = "bad gzip" - header = {"Content-Encoding", "gzip"} - response = %HTTPoison.Response{headers: [header], body: encoded_body} - - assert {:error, :data_error} = Api.body(response) - end - - test "returns an error if we have an error instead of a response" do - error = %HTTPoison.Error{} + response = Api.get_json("/cant_connect") - assert ^error = Api.body(error) + assert {:error, %{reason: _}} = response end end end diff --git a/test/mbta/headers_test.exs b/test/mbta/headers_test.exs deleted file mode 100644 index e005424b71..0000000000 --- a/test/mbta/headers_test.exs +++ /dev/null @@ -1,48 +0,0 @@ -defmodule MBTA.Api.HeadersTest do - use ExUnit.Case - - import Test.Support.EnvHelpers - - alias MBTA.Headers - - setup do - reassign_env(:dotcom, :enable_experimental_features, "false") - - :ok - end - - test "always adds api header" do - assert Headers.build("API_KEY") |> Enum.map(&elem(&1, 0)) == [ - "x-api-key", - "MBTA-Version" - ] - - assert Headers.build("API_KEY") |> Enum.map(&elem(&1, 0)) == [ - "x-api-key", - "MBTA-Version" - ] - end - - test "uses application config for API key version" do - reassign_env(:dotcom, :v3_api_version, "3005-01-02") - - assert Headers.build("API_KEY") == [ - {"x-api-key", "API_KEY"}, - {"MBTA-Version", "3005-01-02"} - ] - end - - test "adds experimental features header if application config is set" do - Application.put_env(:dotcom, :enable_experimental_features, "true") - - assert Headers.build("API_KEY") - |> Keyword.take(["x-enable-experimental-features"]) == [ - {"x-enable-experimental-features", "true"} - ] - - Application.put_env(:dotcom, :enable_experimental_features, nil) - - assert Headers.build("API_KEY") - |> Keyword.take(["x-enable-experimental-features"]) == [] - end -end diff --git a/test/mbta/sentry_extra_test.exs b/test/mbta/sentry_extra_test.exs deleted file mode 100644 index f524bbc319..0000000000 --- a/test/mbta/sentry_extra_test.exs +++ /dev/null @@ -1,68 +0,0 @@ -defmodule MBTA.SentryExtraTest do - use ExUnit.Case, async: false - - import Mox - - alias MBTA.SentryExtra - - setup :set_mox_global - setup :verify_on_exit! - - @process_dictionary_key :sentry_context - @url Faker.Internet.url() - - describe "log_context/2" do - test "one message is one entry in process dictionary" do - expects = %{extra: %{"A-1" => "B"}} - assert nil == SentryExtra.log_context("A", "B") - assert expects == Process.get(@process_dictionary_key) - end - - test "input can be an anonymous function" do - expects = %{extra: %{"A-1" => "B"}} - assert nil == SentryExtra.log_context("A", fn -> "B" end) - assert expects == Process.get(@process_dictionary_key) - end - - test "52 messages is 2 entries in the process dictionary" do - expects = %{extra: %{"A-1" => "51", "A-2" => "52"}} - for n <- 1..52, do: SentryExtra.log_context("A", Integer.to_string(n)) - assert expects == Process.get(@process_dictionary_key) - end - end - - describe "check conditions through MBTA.Api.get_json/3" do - test "bad json response" do - expects = "MBTA.Api.get_json_response url=\"/bad_json\" params={} response={data: garbage}" - - expect(HTTPoison.Mock, :get, fn _, _, _ -> - {:ok, %HTTPoison.Response{status_code: 200, body: "{data: garbage}"}} - end) - - MBTA.Api.get_json("/bad_json", [], base_url: @url) - - assert expects == Process.get(@process_dictionary_key).extra["api-response-error-2"] - end - - test "bad server response" do - expects = "MBTA.Api.get_json_response url=\"/bad_response\" params={} response=" - - expect(HTTPoison.Mock, :get, fn _, _, _ -> - {:ok, %HTTPoison.Response{status_code: 500, body: ""}} - end) - - MBTA.Api.get_json("/bad_response", [], base_url: @url) - assert expects == Process.get(@process_dictionary_key).extra["api-response-error-2"] - end - - test "can't connect returns an error" do - expect(HTTPoison.Mock, :get, fn _, _, _ -> - {:error, %HTTPoison.Error{reason: :econnrefused}} - end) - - MBTA.Api.get_json("/cant_connect", [], base_url: @url) - - assert Process.get(@process_dictionary_key).extra["api-response-1"] =~ "econnrefused" - end - end -end diff --git a/test/stops/api_test.exs b/test/stops/api_test.exs index bfd3567279..b96d69afb2 100644 --- a/test/stops/api_test.exs +++ b/test/stops/api_test.exs @@ -110,8 +110,8 @@ defmodule Stops.ApiTest do end test "returns an error if the API returns an error" do - expect(MBTA.Api.Mock, :get_json, fn _, _, _ -> - {:error, %HTTPoison.Error{reason: :econnrefused}} + expect(MBTA.Api.Mock, :get_json, fn _, _ -> + {:error, %{reason: :econnrefused}} end) assert {:error, _} = by_gtfs_id("error stop") @@ -120,7 +120,7 @@ defmodule Stops.ApiTest do test "all/0 returns error if API returns error" do expect(MBTA.Api.Mock, :get_json, fn _, _ -> - {:error, %HTTPoison.Error{reason: :econnrefused}} + {:error, %{reason: :econnrefused}} end) assert {:error, _} = all() @@ -128,7 +128,7 @@ defmodule Stops.ApiTest do test "by_route returns an error tuple if the V3 API returns an error" do expect(MBTA.Api.Mock, :get_json, fn _, _ -> - {:error, %HTTPoison.Error{reason: :econnrefused}} + {:error, %{reason: :econnrefused}} end) assert {:error, _} = by_route({"1", 0, []}) @@ -140,7 +140,7 @@ defmodule Stops.ApiTest do test "by_trip returns an empty list if the V3 API returns an error" do expect(MBTA.Api.Mock, :get_json, fn _, _ -> - {:ok, %HTTPoison.Response{status_code: 500, body: ""}} + {:ok, %Req.Response{status: 500, body: ""}} end) assert [] = by_trip("1") diff --git a/test/support/behaviours/req/behaviour.ex b/test/support/behaviours/req/behaviour.ex new file mode 100644 index 0000000000..79294fe847 --- /dev/null +++ b/test/support/behaviours/req/behaviour.ex @@ -0,0 +1,11 @@ +defmodule Req.Behaviour do + @moduledoc """ + Defines a behaviour for Req. + """ + + @type url() :: URI.t() | String.t() + + @callback new(options :: keyword()) :: Req.Request.t() + @callback get(url() | keyword() | Req.Request.t(), options :: keyword()) :: + {:ok, Req.Response.t()} | {:error, Exception.t()} +end diff --git a/test/support/mocks.ex b/test/support/mocks.ex index 91b031201c..8777111c32 100644 --- a/test/support/mocks.ex +++ b/test/support/mocks.ex @@ -2,6 +2,7 @@ # External Mox.defmock(HTTPoison.Mock, for: HTTPoison.Base) +Mox.defmock(Req.Mock, for: Req.Behaviour) # Internal Mox.defmock(Dotcom.Redis.Mock, for: Dotcom.Redis.Behaviour) From abd3f3e75e55fbc89889cdf18f87447dc15535bd Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Fri, 19 Apr 2024 15:58:32 -0500 Subject: [PATCH 02/19] collect stats from finch --- config/runtime.exs | 4 +-- lib/mbta/api.ex | 2 +- lib/mbta/api/stats.ex | 58 ++++++++++++++++++++++++++++++++++++++++++ lib/mbta/api/stream.ex | 2 +- 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 lib/mbta/api/stats.ex diff --git a/config/runtime.exs b/config/runtime.exs index 4ea4c1e387..6913c52d3c 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -109,6 +109,7 @@ end config :dotcom, :mbta_api, base_url: System.get_env("MBTA_API_BASE_URL"), + enable_experimental_features: System.get_env("MBTA_API_ENABLE_EXPERIMENTAL_FEATURES"), key: System.get_env("MBTA_API_KEY"), version: System.get_env("MBTA_API_VERSION", "2019-07-01") @@ -207,9 +208,6 @@ config :dotcom, DotcomWeb.ViewHelpers, google_tag_manager_auth: System.get_env("GOOGLE_TAG_MANAGER_AUTH"), google_tag_manager_preview: System.get_env("GOOGLE_TAG_MANAGER_PREVIEW") -config :dotcom, - enable_experimental_features: System.get_env("ENABLE_EXPERIMENTAL_FEATURES") - config :recaptcha, public_key: System.get_env("RECAPTCHA_PUBLIC_KEY"), secret: System.get_env("RECAPTCHA_PRIVATE_KEY") diff --git a/lib/mbta/api.ex b/lib/mbta/api.ex index 5ad1ff19ad..6a88698fee 100644 --- a/lib/mbta/api.ex +++ b/lib/mbta/api.ex @@ -23,7 +23,7 @@ defmodule MBTA.Api do headers: [ {"MBTA-Version", config[:version]}, {"x-api-key", config[:key]}, - {"x-enable-experimental-features", "true"} + {"x-enable-experimental-features", config[:enable_experimental_features]} ] ) end diff --git a/lib/mbta/api/stats.ex b/lib/mbta/api/stats.ex new file mode 100644 index 0000000000..afe7fe8fd3 --- /dev/null +++ b/lib/mbta/api/stats.ex @@ -0,0 +1,58 @@ +defmodule MBTA.Api.Stats do + use Agent + + def start_link(initial_value \\ %{}) do + :telemetry.attach("finch-recv-stop", [:finch, :recv, :stop], &__MODULE__.handle_event/4, nil) + + Agent.start_link(fn -> initial_value end, name: __MODULE__) + end + + def handle_event(_name, measurement, metadata, _config) do + path = path_to_atom(metadata.request.path) + status = status_to_atom(metadata.status) + duration = measurement[:duration] + + Agent.update(__MODULE__, fn state -> + if Kernel.get_in(state, [path, status]) do + Kernel.update_in(state, [path, status], &(&1 ++ [duration])) + else + Kernel.put_in(state, [Access.key(path, %{}), status], [duration]) + end + end) + end + + def dispatch_stats() do + Agent.get(__MODULE__, & &1) + |> Enum.each(fn {path, stats} -> + Enum.each(stats, fn {status, durations} -> + count = Enum.count(durations) + + avg = + durations + |> Enum.sum() + |> Kernel.div(count) + |> Kernel.div(1000) + + :telemetry.execute([:mbta_api, :request], %{count: count, avg: avg}, %{ + path: path, + status: status + }) + end) + end) + + Agent.update(__MODULE__, fn _ -> %{} end) + end + + defp path_to_atom(path) do + path + |> String.replace(~r{^/|/$}, "") + |> String.replace(~r{/}, "_") + |> String.to_atom() + end + + defp status_to_atom(status) do + status + |> Integer.to_string() + |> String.to_atom() + end +end diff --git a/lib/mbta/api/stream.ex b/lib/mbta/api/stream.ex index 9f402a609f..6ce3e988cf 100644 --- a/lib/mbta/api/stream.ex +++ b/lib/mbta/api/stream.ex @@ -104,7 +104,7 @@ defmodule MBTA.Api.Stream do headers = [ {"MBTA-Version", config[:version]}, {"x-api-key", config[:key]}, - {"x-enable-experimental-features", "true"} + {"x-enable-experimental-features", config[:enable_experimental_features]} ] Keyword.put(opts, :headers, headers) From 251fccc892416814c4d8661daeafdd95d93356ab Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Mon, 22 Apr 2024 08:02:11 -0500 Subject: [PATCH 03/19] cleanup module --- lib/mbta/api/stats.ex | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/lib/mbta/api/stats.ex b/lib/mbta/api/stats.ex index afe7fe8fd3..3149781bf3 100644 --- a/lib/mbta/api/stats.ex +++ b/lib/mbta/api/stats.ex @@ -22,27 +22,32 @@ defmodule MBTA.Api.Stats do end def dispatch_stats() do - Agent.get(__MODULE__, & &1) - |> Enum.each(fn {path, stats} -> - Enum.each(stats, fn {status, durations} -> - count = Enum.count(durations) - - avg = - durations - |> Enum.sum() - |> Kernel.div(count) - |> Kernel.div(1000) - - :telemetry.execute([:mbta_api, :request], %{count: count, avg: avg}, %{ - path: path, - status: status - }) - end) - end) + Enum.each(Agent.get(__MODULE__, & &1), &dispatch_path/1) Agent.update(__MODULE__, fn _ -> %{} end) end + defp dispatch_path({path, stats}) do + Enum.each(stats, fn {status, durations} -> + dispatch_stat(path, status, durations) + end) + end + + defp dispatch_stat(path, status, durations) do + count = Enum.count(durations) + + avg = + durations + |> Enum.sum() + |> Kernel.div(count) + |> Kernel.div(1000) + + :telemetry.execute([:mbta_api, :request], %{count: count, avg: avg}, %{ + path: path, + status: status + }) + end + defp path_to_atom(path) do path |> String.replace(~r{^/|/$}, "") From d9b7c80e164b67cc77f5ee57ab896f201124ae67 Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Mon, 22 Apr 2024 08:24:47 -0500 Subject: [PATCH 04/19] lower coveralls back down to 60 since it keeps fluctuating below 62 --- coveralls.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coveralls.json b/coveralls.json index 10711359d7..a06860dd4f 100644 --- a/coveralls.json +++ b/coveralls.json @@ -1,6 +1,6 @@ { "coverage_options": { - "minimum_coverage": 62, + "minimum_coverage": 60, "treat_no_relevant_lines_as_covered": true }, "custom_stop_words": [ From 6598372ef99e8ee0916ceb03658265f78fcdaf9e Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Mon, 22 Apr 2024 14:00:48 -0500 Subject: [PATCH 05/19] 100% test coverage for mbta api module --- lib/mbta/api/route_patterns.ex | 4 +- lib/mbta/api/routes.ex | 28 +++--- lib/mbta/api/stats.ex | 7 +- lib/mbta/api/stream.ex | 12 ++- mix.exs | 1 + mix.lock | 1 + test/mbta/api/alerts_test.exs | 25 ++++++ test/mbta/api/facilities_test.exs | 43 +++++++++ test/mbta/api/predictions_test.exs | 26 ++++++ test/mbta/api/route_patterns_test.exs | 42 +++++++++ test/mbta/api/routes_test.exs | 97 +++++++++++++++++++++ test/mbta/api/schedules_test.exs | 25 ++++++ test/mbta/api/services_test.exs | 42 +++++++++ test/mbta/api/shapes_test.exs | 42 +++++++++ test/mbta/api/stats_test.exs | 49 +++++++++++ test/mbta/api/stream_test.exs | 78 +++++++++++++++++ test/mbta/api/trips_test.exs | 45 ++++++++++ test/mbta/api_test.exs | 4 +- test/mbta/stream_test.exs | 34 -------- test/predictions/stream_supervisor_test.exs | 2 +- 20 files changed, 546 insertions(+), 61 deletions(-) create mode 100644 test/mbta/api/alerts_test.exs create mode 100644 test/mbta/api/facilities_test.exs create mode 100644 test/mbta/api/predictions_test.exs create mode 100644 test/mbta/api/route_patterns_test.exs create mode 100644 test/mbta/api/routes_test.exs create mode 100644 test/mbta/api/schedules_test.exs create mode 100644 test/mbta/api/services_test.exs create mode 100644 test/mbta/api/shapes_test.exs create mode 100644 test/mbta/api/stats_test.exs create mode 100644 test/mbta/api/stream_test.exs create mode 100644 test/mbta/api/trips_test.exs delete mode 100644 test/mbta/stream_test.exs diff --git a/lib/mbta/api/route_patterns.ex b/lib/mbta/api/route_patterns.ex index dee81c0460..4dc69ca341 100644 --- a/lib/mbta/api/route_patterns.ex +++ b/lib/mbta/api/route_patterns.ex @@ -15,7 +15,7 @@ defmodule MBTA.Api.RoutePatterns do end @spec get(Route.id_t(), keyword()) :: api_response_t() - def get(id, opts \\ []) do - @mbta_api.get_json("/route_patterns/#{id}", opts) + def get(id, params \\ []) do + @mbta_api.get_json("/route_patterns/#{id}", params) end end diff --git a/lib/mbta/api/routes.ex b/lib/mbta/api/routes.ex index ac273d4a4a..a736b31e7f 100644 --- a/lib/mbta/api/routes.ex +++ b/lib/mbta/api/routes.ex @@ -11,34 +11,34 @@ defmodule MBTA.Api.Routes do @type api_response_t() :: JsonApi.t() | {:error, any} @spec all(keyword()) :: api_response_t() - def all(opts \\ []) do - @mbta_api.get_json("/routes/", opts) + def all(params \\ []) do + @mbta_api.get_json("/routes/", params) end @spec get(Route.id_t(), keyword()) :: api_response_t() - def get(id, opts \\ []) do - @mbta_api.get_json("/routes/#{id}", opts) + def get(id, params \\ []) do + @mbta_api.get_json("/routes/#{id}", params) end @spec by_type(Route.type_int(), keyword()) :: api_response_t() - def by_type(type, opts \\ []) do - opts = put_in(opts[:type], type) + def by_type(type, params \\ []) do + params = put_in(params[:type], type) - @mbta_api.get_json("/routes/", opts) + @mbta_api.get_json("/routes/", params) end @spec by_stop(Stop.id_t(), keyword()) :: api_response_t() - def by_stop(stop_id, opts \\ []) do - opts = put_in(opts[:stop], stop_id) + def by_stop(stop_id, params \\ []) do + params = put_in(params[:stop], stop_id) - @mbta_api.get_json("/routes/", opts) + @mbta_api.get_json("/routes/", params) end @spec by_stop_and_direction(Stop.id_t(), 0 | 1, keyword()) :: api_response_t() - def by_stop_and_direction(stop_id, direction_id, opts \\ []) do - opts = put_in(opts[:stop], stop_id) - opts = put_in(opts[:direction_id], direction_id) + def by_stop_and_direction(stop_id, direction_id, params \\ []) do + params = put_in(params[:stop], stop_id) + params = put_in(params[:direction_id], direction_id) - @mbta_api.get_json("/routes/", opts) + @mbta_api.get_json("/routes/", params) end end diff --git a/lib/mbta/api/stats.ex b/lib/mbta/api/stats.ex index 3149781bf3..2e55960325 100644 --- a/lib/mbta/api/stats.ex +++ b/lib/mbta/api/stats.ex @@ -1,4 +1,10 @@ defmodule MBTA.Api.Stats do + @moduledoc """ + This Agent attaches to telemetry events emitted by Finch and aggregates them by path and status. + + When `dispatch_stats/0` is called, it sends the aggregated data to the `:mbta_api` telemetry event. + """ + use Agent def start_link(initial_value \\ %{}) do @@ -40,7 +46,6 @@ defmodule MBTA.Api.Stats do durations |> Enum.sum() |> Kernel.div(count) - |> Kernel.div(1000) :telemetry.execute([:mbta_api, :request], %{count: count, avg: avg}, %{ path: path, diff --git a/lib/mbta/api/stream.ex b/lib/mbta/api/stream.ex index 6ce3e988cf..64752649d9 100644 --- a/lib/mbta/api/stream.ex +++ b/lib/mbta/api/stream.ex @@ -118,11 +118,9 @@ defmodule MBTA.Api.Stream do } end - @spec event(String.t()) :: Event.event() - for atom <- ~w(reset add update remove)a do - str = Atom.to_string(atom) - defp event(unquote(str)), do: unquote(atom) - end - - defp event("error"), do: :unknown + defp event("add"), do: :add + defp event("remove"), do: :remove + defp event("update"), do: :update + defp event("reset"), do: :reset + defp event(_), do: :unknown end diff --git a/mix.exs b/mix.exs index 7260a1901f..279fb2da75 100644 --- a/mix.exs +++ b/mix.exs @@ -145,6 +145,7 @@ defmodule DotCom.Mixfile do {:telemetry_metrics, "0.6.2"}, {:telemetry_metrics_statsd, "0.7.0"}, {:telemetry_poller, "1.1.0"}, + {:telemetry_test, "0.1.2", only: [:test]}, # latest version is 3.7.11; cannot upgrade because tests fail {:timex, "3.1.24"}, {:unrooted_polytree, "0.1.1"}, diff --git a/mix.lock b/mix.lock index dbc52b2bc0..c7f9d99d57 100644 --- a/mix.lock +++ b/mix.lock @@ -102,6 +102,7 @@ "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.2", "2caabe9344ec17eafe5403304771c3539f3b6e2f7fb6a6f602558c825d0d0bfb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b43db0dc33863930b9ef9d27137e78974756f5f198cae18409970ed6fa5b561"}, "telemetry_metrics_statsd": {:hex, :telemetry_metrics_statsd, "0.7.0", "92732fae63db31ef2508df6faee7d81401883e33f2976715a82f296a33a45cee", [:mix], [{:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "797e34a856376dfd4e96347da0f747fcff4e0cadf6e6f0f989598f563cad05ff"}, "telemetry_poller": {:hex, :telemetry_poller, "1.1.0", "58fa7c216257291caaf8d05678c8d01bd45f4bdbc1286838a28c4bb62ef32999", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9eb9d9cbfd81cbd7cdd24682f8711b6e2b691289a0de6826e58452f28c103c8f"}, + "telemetry_test": {:hex, :telemetry_test, "0.1.2", "122d927567c563cf57773105fa8104ae4299718ec2cbdddcf6776562c7488072", [:mix], [{:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7bd41a49ecfd33ecd82d2c7edae19a5736f0d2150206d0ee290dcf3885d0e14d"}, "tesla": {:hex, :tesla, "1.8.0", "d511a4f5c5e42538d97eef7c40ec4f3e44effdc5068206f42ed859e09e51d1fd", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "10501f360cd926a309501287470372af1a6e1cbed0f43949203a4c13300bc79f"}, "timex": {:hex, :timex, "3.1.24", "d198ae9783ac807721cca0c5535384ebdf99da4976be8cefb9665a9262a1e9e3", [:mix], [{:combine, "~> 0.7", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "ca852258d788542c263b12dbf55375fe2ccf5674e7b20995e3d84d2d4412bc0f"}, "tzdata": {:hex, :tzdata, "0.5.22", "f2ba9105117ee0360eae2eca389783ef7db36d533899b2e84559404dbc77ebb8", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "cd66c8a1e6a9e121d1f538b01bef459334bb4029a1ffb4eeeb5e4eae0337e7b6"}, diff --git a/test/mbta/api/alerts_test.exs b/test/mbta/api/alerts_test.exs new file mode 100644 index 0000000000..e77d56af9e --- /dev/null +++ b/test/mbta/api/alerts_test.exs @@ -0,0 +1,25 @@ +defmodule MBTA.Api.AlertsTest do + use ExUnit.Case, async: false + + import Mox + + alias MBTA.Api.{Alerts, Mock} + + setup :set_mox_global + setup :verify_on_exit! + + test "all/0 returns a list of alerts" do + # Setup + expect(Mock, :get_json, fn url, _ -> + assert url == "/alerts/" + + [] + end) + + # Exercise + alerts = Alerts.all() + + # Verify + assert alerts == [] + end +end diff --git a/test/mbta/api/facilities_test.exs b/test/mbta/api/facilities_test.exs new file mode 100644 index 0000000000..55e833ff39 --- /dev/null +++ b/test/mbta/api/facilities_test.exs @@ -0,0 +1,43 @@ +defmodule MBTA.Api.FacilitiesTest do + use ExUnit.Case, async: false + + import Mox + + alias MBTA.Api.{Facilities, Mock} + + setup :set_mox_global + setup :verify_on_exit! + + test "all/0 returns a list of facilities" do + # Setup + expect(Mock, :get_json, fn url, _ -> + assert url == "/facilities/" + + [] + end) + + # Exercise + facilities = Facilities.all() + + # Verify + assert facilities == [] + end + + test "filter_by/1 sends filters as query parameters" do + # Setup + expect(Mock, :get_json, fn url, params -> + assert url == "/facilities/" + + assert Enum.sort(params) == + Enum.sort([{"filter[stop_id]", "place-sstat"}, {"filter[route_type]", "0"}]) + + [] + end) + + # Exercise + facilities = Facilities.filter_by(%{stop_id: "place-sstat", route_type: "0"}) + + # Verify + assert facilities == [] + end +end diff --git a/test/mbta/api/predictions_test.exs b/test/mbta/api/predictions_test.exs new file mode 100644 index 0000000000..4e24ab65bb --- /dev/null +++ b/test/mbta/api/predictions_test.exs @@ -0,0 +1,26 @@ +defmodule MBTA.Api.PredictionsTest do + use ExUnit.Case, async: false + + import Mox + + alias MBTA.Api.{Predictions, Mock} + + setup :set_mox_global + setup :verify_on_exit! + + test "all/1 returns a list of predictions" do + # Setup + expect(Mock, :get_json, fn url, params -> + assert url == "/predictions/" + assert Enum.sort(params) == Enum.sort(foo: 1, bar: 2) + + [] + end) + + # Exercise + predictions = Predictions.all(foo: 1, bar: 2) + + # Verify + assert predictions == [] + end +end diff --git a/test/mbta/api/route_patterns_test.exs b/test/mbta/api/route_patterns_test.exs new file mode 100644 index 0000000000..1f27e3bd3d --- /dev/null +++ b/test/mbta/api/route_patterns_test.exs @@ -0,0 +1,42 @@ +defmodule Mbta.Api.RoutePatternsTest do + use ExUnit.Case, async: false + + import Mox + + alias MBTA.Api.{Mock, RoutePatterns} + + setup :set_mox_global + setup :verify_on_exit! + + test "all/0 returns a list of route patterns" do + # Setup + expect(Mock, :get_json, fn url, _ -> + assert url == "/route_patterns/" + + [] + end) + + # Exercise + route_patterns = RoutePatterns.all() + + # Verify + assert route_patterns == [] + end + + test "get/1 returns a route pattern" do + # Setup + id = 1 + + expect(Mock, :get_json, fn url, _ -> + assert url == "/route_patterns/#{id}" + + %{} + end) + + # Exercise + route_pattern = RoutePatterns.get(1) + + # Verify + assert route_pattern == %{} + end +end diff --git a/test/mbta/api/routes_test.exs b/test/mbta/api/routes_test.exs new file mode 100644 index 0000000000..c7554dcc6f --- /dev/null +++ b/test/mbta/api/routes_test.exs @@ -0,0 +1,97 @@ +defmodule MBTA.Api.RoutesTest do + use ExUnit.Case, async: false + + import Mox + + alias MBTA.Api.{Mock, Routes} + + setup :set_mox_global + setup :verify_on_exit! + + test "all/0 returns a list of routes" do + # Setup + expect(Mock, :get_json, fn url, _ -> + assert url == "/routes/" + + [] + end) + + # Exercise + routes = Routes.all() + + # Verify + assert routes == [] + end + + test "get/1 returns a route" do + # Setup + id = :rand.uniform(100) + + expect(Mock, :get_json, fn url, _ -> + assert url == "/routes/#{id}" + + %{} + end) + + # Exercise + route = Routes.get(id) + + # Verify + assert route == %{} + end + + test "by_type/1 sends type as query parameter" do + # Setup + type = :rand.uniform(100) + + expect(Mock, :get_json, fn url, params -> + assert url == "/routes/" + assert Enum.sort(params) == Enum.sort(type: type) + + [] + end) + + # Exercise + routes = Routes.by_type(type) + + # Verify + assert routes == [] + end + + test "by_stop/1 sends stop as query parameter" do + # Setup + stop = Faker.Team.creature() |> String.downcase() + + expect(Mock, :get_json, fn url, params -> + assert url == "/routes/" + assert Enum.sort(params) == Enum.sort(stop: stop) + + [] + end) + + # Exercise + routes = Routes.by_stop(stop) + + # Verify + assert routes == [] + end + + test "by_stop_and_direction/2 sends stop and direction_id as query parameters" do + # Setup + stop = Faker.Team.creature() |> String.downcase() + direction_id = :rand.uniform(1) + + expect(Mock, :get_json, fn url, params -> + assert url == "/routes/" + assert Enum.sort(params) == Enum.sort(stop: stop, direction_id: direction_id) + + [] + end) + + # Exercise + routes = Routes.by_stop_and_direction(stop, direction_id) + + # Verify + assert routes == [] + end +end diff --git a/test/mbta/api/schedules_test.exs b/test/mbta/api/schedules_test.exs new file mode 100644 index 0000000000..9e45894c2c --- /dev/null +++ b/test/mbta/api/schedules_test.exs @@ -0,0 +1,25 @@ +defmodule MBTA.Api.SchedulesTest do + use ExUnit.Case, async: false + + import Mox + + alias MBTA.Api.{Mock, Schedules} + + setup :set_mox_global + setup :verify_on_exit! + + test "all/0 returns a list of schedules" do + # Setup + expect(Mock, :get_json, fn url, _ -> + assert url == "/schedules/" + + [] + end) + + # Exercise + schedules = Schedules.all() + + # Verify + assert schedules == [] + end +end diff --git a/test/mbta/api/services_test.exs b/test/mbta/api/services_test.exs new file mode 100644 index 0000000000..602fb714de --- /dev/null +++ b/test/mbta/api/services_test.exs @@ -0,0 +1,42 @@ +defmodule MBTA.Api.ServicesTest do + use ExUnit.Case, async: false + + import Mox + + alias MBTA.Api.{Mock, Services} + + setup :set_mox_global + setup :verify_on_exit! + + test "all/0 returns a list of services" do + # Setup + expect(Mock, :get_json, fn url, _ -> + assert url == "/services/" + + [] + end) + + # Exercise + services = Services.all() + + # Verify + assert services == [] + end + + test "get/1 returns a service" do + # Setup + id = :rand.uniform(100) + + expect(Mock, :get_json, fn url, _ -> + assert url == "/services/#{id}" + + %{} + end) + + # Exercise + service = Services.get(id) + + # Verify + assert service == %{} + end +end diff --git a/test/mbta/api/shapes_test.exs b/test/mbta/api/shapes_test.exs new file mode 100644 index 0000000000..eaba254634 --- /dev/null +++ b/test/mbta/api/shapes_test.exs @@ -0,0 +1,42 @@ +defmodule MBTA.Api.ShapesTest do + use ExUnit.Case, async: false + + import Mox + + alias MBTA.Api.{Mock, Shapes} + + setup :set_mox_global + setup :verify_on_exit! + + test "all/0 returns a list of shapes" do + # Setup + expect(Mock, :get_json, fn url, _ -> + assert url == "/shapes/" + + [] + end) + + # Exercise + shapes = Shapes.all() + + # Verify + assert shapes == [] + end + + test "by_id/1 returns a shape" do + # Setup + id = Faker.Team.creature() |> String.downcase() + + expect(Mock, :get_json, fn url -> + assert url == "/shapes/#{id}" + + %{} + end) + + # Exercise + shape = Shapes.by_id(id) + + # Verify + assert shape == %{} + end +end diff --git a/test/mbta/api/stats_test.exs b/test/mbta/api/stats_test.exs new file mode 100644 index 0000000000..49b1caae4c --- /dev/null +++ b/test/mbta/api/stats_test.exs @@ -0,0 +1,49 @@ +defmodule MBTA.Api.StatsTest do + use ExUnit.Case + + import TelemetryTest + + alias MBTA.Api.Stats + + setup [:telemetry_listen] + + setup do + {:ok, _} = Stats.start_link() + + :ok + end + + @tag telemetry_listen: [:mbta_api, :request] + test "aggregates and dispatches stats" do + # Setup + # 1 second in nanoseconds + duration = :rand.uniform(1_000_000_000) + + measurement = %{ + duration: duration + } + + metadata = %{ + request: %{ + path: "/#{Faker.Team.creature()}/" + }, + status: Enum.random([200, 404, 500]) + } + + # Exercise + :telemetry.execute([:finch, :recv, :stop], measurement, metadata) + :telemetry.execute([:finch, :recv, :stop], measurement, metadata) + + Stats.dispatch_stats() + + # Verify + assert_receive { + :telemetry_event, + %{ + event: [:mbta_api, :request], + measurements: %{avg: ^duration, count: 2}, + metadata: %{path: _path, status: _status} + } + } + end +end diff --git a/test/mbta/api/stream_test.exs b/test/mbta/api/stream_test.exs new file mode 100644 index 0000000000..d700fc453b --- /dev/null +++ b/test/mbta/api/stream_test.exs @@ -0,0 +1,78 @@ +defmodule MBTA.Api.StreamTest do + use ExUnit.Case, async: false + + alias MBTA.Api.Stream + + describe "build_options" do + test "includes api key" do + Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") + + opts = Stream.build_options(path: "/vehicles") + assert Keyword.get(opts, :url) =~ "/vehicles" + assert <<_::binary>> = Keyword.get(opts, :key) + end + + test "throws error if mbta api base url is missing" do + Application.put_env(:dotcom, :mbta_api, base_url: nil, key: "foo") + + assert_raise ArgumentError, "Missing required configuration for MBTA API", fn -> + Stream.build_options(path: "/vehicles") + end + end + + test "throws error if mbta api key is missing" do + Application.put_env(:dotcom, :mbta_api, base_url: "http://example.com", key: nil) + + assert_raise ArgumentError, "Missing required configuration for MBTA API", fn -> + Stream.build_options(path: "/vehicles") + end + end + end + + describe "start_link" do + @tag :external + test "handles api events" do + {:ok, sses} = + [path: "/vehicles"] + |> Stream.build_options() + |> ServerSentEventStage.start_link() + + {:ok, pid} = Stream.start_link(name: __MODULE__, subscribe_to: sses) + + types = [:add, :remove, :reset, :update] + + known_events? = + [pid] + |> GenStage.stream() + |> Enum.take(4) + |> Enum.all?(fn %Stream.Event{event: type} -> Enum.member?(types, type) end) + + assert known_events? + end + end + + describe "handle_events/3" do + test "sets all known events" do + # Setup + Enum.each([:add, :remove, :reset, :update], fn type -> + # Exercise + event = %ServerSentEventStage.Event{data: %{}, event: Atom.to_string(type)} + result = Stream.handle_events([event], nil, nil) |> Kernel.elem(1) |> List.first() + + # Verify + assert Map.get(result, :event) == type + end) + end + + test "sets an unknown event" do + # Setup + event = %ServerSentEventStage.Event{data: %{}, event: "foo"} + + # Exercise + result = Stream.handle_events([event], nil, nil) + + # Verify + assert result == {:noreply, [%Stream.Event{data: {:error, :invalid}, event: :unknown}], nil} + end + end +end diff --git a/test/mbta/api/trips_test.exs b/test/mbta/api/trips_test.exs new file mode 100644 index 0000000000..c021159855 --- /dev/null +++ b/test/mbta/api/trips_test.exs @@ -0,0 +1,45 @@ +defmodule MBTA.Api.TripsTest do + use ExUnit.Case, async: false + + import Mox + + alias MBTA.Api.{Mock, Trips} + + setup :set_mox_global + setup :verify_on_exit! + + test "by_id/1 returns a trip" do + # Setup + id = Faker.Team.creature() |> String.downcase() + + expect(Mock, :get_json, fn url, _ -> + assert url == "/trips/#{id}" + + %{} + end) + + # Exercise + trip = Trips.by_id(id) + + # Verify + assert trip == %{} + end + + test "by_route/1 sends route as query parameter" do + # Setup + route = Faker.Team.creature() |> String.downcase() + + expect(Mock, :get_json, fn url, params -> + assert url == "/trips/" + assert Enum.sort(params) == Enum.sort(route: route) + + [] + end) + + # Exercise + trips = Trips.by_route(route) + + # Verify + assert trips == [] + end +end diff --git a/test/mbta/api_test.exs b/test/mbta/api_test.exs index 40c28e2e9c..d90db3e3c5 100644 --- a/test/mbta/api_test.exs +++ b/test/mbta/api_test.exs @@ -1,10 +1,10 @@ defmodule MBTA.ApiTest do use ExUnit.Case, async: false - alias MBTA.Api - import Mox + alias MBTA.Api + setup :set_mox_global setup :verify_on_exit! diff --git a/test/mbta/stream_test.exs b/test/mbta/stream_test.exs deleted file mode 100644 index 705ccac631..0000000000 --- a/test/mbta/stream_test.exs +++ /dev/null @@ -1,34 +0,0 @@ -defmodule MBTA.Api.StreamTest do - use ExUnit.Case - - describe "build_options" do - @tag :external - test "includes api key" do - opts = MBTA.Api.Stream.build_options(path: "/vehicles") - assert Keyword.get(opts, :url) =~ "/vehicles" - assert <<_::binary>> = Keyword.get(opts, :api_key) - end - end - - describe "start_link" do - @tag :external - test "handles api events" do - {:ok, sses} = - [path: "/vehicles"] - |> MBTA.Api.Stream.build_options() - |> ServerSentEventStage.start_link() - - {:ok, pid} = MBTA.Api.Stream.start_link(name: __MODULE__, subscribe_to: sses) - - types = [:add, :remove, :reset, :update] - - known_events? = - [pid] - |> GenStage.stream() - |> Enum.take(4) - |> Enum.all?(fn %MBTA.Api.Stream.Event{event: type} -> Enum.member?(types, type) end) - - assert known_events? - end - end -end diff --git a/test/predictions/stream_supervisor_test.exs b/test/predictions/stream_supervisor_test.exs index c2b48432f6..377072d58c 100644 --- a/test/predictions/stream_supervisor_test.exs +++ b/test/predictions/stream_supervisor_test.exs @@ -1,5 +1,5 @@ defmodule Predictions.StreamSupervisorTest do - use ExUnit.Case + use ExUnit.Case, async: false alias Predictions.StreamSupervisor setup_all do From b6d53c0d09f8a8a973cd3d0e4c9c7f19a51a4fa8 Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Mon, 22 Apr 2024 14:56:11 -0500 Subject: [PATCH 06/19] docs --- lib/mbta/api/stats.ex | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/mbta/api/stats.ex b/lib/mbta/api/stats.ex index 2e55960325..990b589fbd 100644 --- a/lib/mbta/api/stats.ex +++ b/lib/mbta/api/stats.ex @@ -1,18 +1,22 @@ defmodule MBTA.Api.Stats do @moduledoc """ This Agent attaches to telemetry events emitted by Finch and aggregates them by path and status. - - When `dispatch_stats/0` is called, it sends the aggregated data to the `:mbta_api` telemetry event. """ use Agent + @doc """ + Starts the Agent and attaches to `[:finch, :recv, :stop]` telemetry events. + """ def start_link(initial_value \\ %{}) do :telemetry.attach("finch-recv-stop", [:finch, :recv, :stop], &__MODULE__.handle_event/4, nil) Agent.start_link(fn -> initial_value end, name: __MODULE__) end + @doc """ + Handles telemetry events and aggregates them by path and status. + """ def handle_event(_name, measurement, metadata, _config) do path = path_to_atom(metadata.request.path) status = status_to_atom(metadata.status) @@ -27,6 +31,11 @@ defmodule MBTA.Api.Stats do end) end + @doc """ + Dispatches the aggregated stats to the `[:mbta_api, :request]` telemetry event. + + Resets the Agent state after dispatching the stats. + """ def dispatch_stats() do Enum.each(Agent.get(__MODULE__, & &1), &dispatch_path/1) From 786504f43658481089e4b849e7c6dab0db1cb85c Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Mon, 22 Apr 2024 15:03:26 -0500 Subject: [PATCH 07/19] make test more generic --- test/mbta/api/facilities_test.exs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/mbta/api/facilities_test.exs b/test/mbta/api/facilities_test.exs index 55e833ff39..989317952d 100644 --- a/test/mbta/api/facilities_test.exs +++ b/test/mbta/api/facilities_test.exs @@ -25,17 +25,20 @@ defmodule MBTA.Api.FacilitiesTest do test "filter_by/1 sends filters as query parameters" do # Setup + stop_id = Faker.Team.creature() |> String.downcase() + route_type = Enum.random(["0", "1", "2", "3"]) + expect(Mock, :get_json, fn url, params -> assert url == "/facilities/" assert Enum.sort(params) == - Enum.sort([{"filter[stop_id]", "place-sstat"}, {"filter[route_type]", "0"}]) + Enum.sort([{"filter[stop_id]", stop_id}, {"filter[route_type]", route_type}]) [] end) # Exercise - facilities = Facilities.filter_by(%{stop_id: "place-sstat", route_type: "0"}) + facilities = Facilities.filter_by(%{stop_id: stop_id, route_type: route_type}) # Verify assert facilities == [] From b5cbf5ad8376c59713fc869bcbd01b25d9ecff63 Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Mon, 22 Apr 2024 15:48:19 -0500 Subject: [PATCH 08/19] correct schema type --- test/mbta/api_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mbta/api_test.exs b/test/mbta/api_test.exs index d90db3e3c5..bec34b4f8b 100644 --- a/test/mbta/api_test.exs +++ b/test/mbta/api_test.exs @@ -15,7 +15,7 @@ defmodule MBTA.ApiTest do end) expect(Req.Mock, :get, fn _, _ -> - {:ok, %HTTPoison.Response{status_code: 200, body: ~s({"data": []})}} + {:ok, %Req.Response{status: 200, body: ~s({"data": []})}} end) response = Api.get_json("/normal_response") From 053d5a9dfbbe81fb3f601621b2e7832d37f8fd1c Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Mon, 22 Apr 2024 16:14:26 -0500 Subject: [PATCH 09/19] flaky test --- test/mbta/api/stream_test.exs | 8 +++++++- test/predictions/stream_supervisor_test.exs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/mbta/api/stream_test.exs b/test/mbta/api/stream_test.exs index d700fc453b..ac46dcbdc3 100644 --- a/test/mbta/api/stream_test.exs +++ b/test/mbta/api/stream_test.exs @@ -1,9 +1,15 @@ defmodule MBTA.Api.StreamTest do - use ExUnit.Case, async: false + use ExUnit.Case alias MBTA.Api.Stream describe "build_options" do + setup do + on_exit(fn -> + Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") + end) + end + test "includes api key" do Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") diff --git a/test/predictions/stream_supervisor_test.exs b/test/predictions/stream_supervisor_test.exs index 377072d58c..c2b48432f6 100644 --- a/test/predictions/stream_supervisor_test.exs +++ b/test/predictions/stream_supervisor_test.exs @@ -1,5 +1,5 @@ defmodule Predictions.StreamSupervisorTest do - use ExUnit.Case, async: false + use ExUnit.Case alias Predictions.StreamSupervisor setup_all do From 2183b08f163a87ab45ca49ac34b1ebdd3fd46ecd Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Mon, 22 Apr 2024 16:21:08 -0500 Subject: [PATCH 10/19] make async --- test/mbta/api/stream_test.exs | 2 +- test/predictions/stream_supervisor_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/mbta/api/stream_test.exs b/test/mbta/api/stream_test.exs index ac46dcbdc3..2ed44fbd41 100644 --- a/test/mbta/api/stream_test.exs +++ b/test/mbta/api/stream_test.exs @@ -1,5 +1,5 @@ defmodule MBTA.Api.StreamTest do - use ExUnit.Case + use ExUnit.Case, async: false alias MBTA.Api.Stream diff --git a/test/predictions/stream_supervisor_test.exs b/test/predictions/stream_supervisor_test.exs index c2b48432f6..377072d58c 100644 --- a/test/predictions/stream_supervisor_test.exs +++ b/test/predictions/stream_supervisor_test.exs @@ -1,5 +1,5 @@ defmodule Predictions.StreamSupervisorTest do - use ExUnit.Case + use ExUnit.Case, async: false alias Predictions.StreamSupervisor setup_all do From a6ba0d946a1db263b25f464cfedb48325f4b605e Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Tue, 23 Apr 2024 08:53:02 -0500 Subject: [PATCH 11/19] one more try --- test/mbta/api/stream_test.exs | 10 ++++------ test/predictions/stream_supervisor_test.exs | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/test/mbta/api/stream_test.exs b/test/mbta/api/stream_test.exs index 2ed44fbd41..aa8cc154cf 100644 --- a/test/mbta/api/stream_test.exs +++ b/test/mbta/api/stream_test.exs @@ -4,12 +4,6 @@ defmodule MBTA.Api.StreamTest do alias MBTA.Api.Stream describe "build_options" do - setup do - on_exit(fn -> - Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") - end) - end - test "includes api key" do Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") @@ -24,6 +18,8 @@ defmodule MBTA.Api.StreamTest do assert_raise ArgumentError, "Missing required configuration for MBTA API", fn -> Stream.build_options(path: "/vehicles") end + + Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") end test "throws error if mbta api key is missing" do @@ -32,6 +28,8 @@ defmodule MBTA.Api.StreamTest do assert_raise ArgumentError, "Missing required configuration for MBTA API", fn -> Stream.build_options(path: "/vehicles") end + + Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") end end diff --git a/test/predictions/stream_supervisor_test.exs b/test/predictions/stream_supervisor_test.exs index 377072d58c..a5aae988dd 100644 --- a/test/predictions/stream_supervisor_test.exs +++ b/test/predictions/stream_supervisor_test.exs @@ -1,5 +1,6 @@ defmodule Predictions.StreamSupervisorTest do use ExUnit.Case, async: false + alias Predictions.StreamSupervisor setup_all do From ddb282af1328d22a84acd5979d83a17e31393251 Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Tue, 23 Apr 2024 09:00:05 -0500 Subject: [PATCH 12/19] remove tests causing flakiness --- test/mbta/api/stream_test.exs | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/test/mbta/api/stream_test.exs b/test/mbta/api/stream_test.exs index aa8cc154cf..a37bd1bb6e 100644 --- a/test/mbta/api/stream_test.exs +++ b/test/mbta/api/stream_test.exs @@ -3,36 +3,6 @@ defmodule MBTA.Api.StreamTest do alias MBTA.Api.Stream - describe "build_options" do - test "includes api key" do - Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") - - opts = Stream.build_options(path: "/vehicles") - assert Keyword.get(opts, :url) =~ "/vehicles" - assert <<_::binary>> = Keyword.get(opts, :key) - end - - test "throws error if mbta api base url is missing" do - Application.put_env(:dotcom, :mbta_api, base_url: nil, key: "foo") - - assert_raise ArgumentError, "Missing required configuration for MBTA API", fn -> - Stream.build_options(path: "/vehicles") - end - - Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") - end - - test "throws error if mbta api key is missing" do - Application.put_env(:dotcom, :mbta_api, base_url: "http://example.com", key: nil) - - assert_raise ArgumentError, "Missing required configuration for MBTA API", fn -> - Stream.build_options(path: "/vehicles") - end - - Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") - end - end - describe "start_link" do @tag :external test "handles api events" do From 590cf8c02e777c49cd4984210bae62523bd0a15f Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Tue, 23 Apr 2024 09:09:33 -0500 Subject: [PATCH 13/19] ... --- test/mbta/api/stream_test.exs | 26 +++++++++++++++++++++ test/predictions/stream_supervisor_test.exs | 8 +++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/test/mbta/api/stream_test.exs b/test/mbta/api/stream_test.exs index a37bd1bb6e..d700fc453b 100644 --- a/test/mbta/api/stream_test.exs +++ b/test/mbta/api/stream_test.exs @@ -3,6 +3,32 @@ defmodule MBTA.Api.StreamTest do alias MBTA.Api.Stream + describe "build_options" do + test "includes api key" do + Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") + + opts = Stream.build_options(path: "/vehicles") + assert Keyword.get(opts, :url) =~ "/vehicles" + assert <<_::binary>> = Keyword.get(opts, :key) + end + + test "throws error if mbta api base url is missing" do + Application.put_env(:dotcom, :mbta_api, base_url: nil, key: "foo") + + assert_raise ArgumentError, "Missing required configuration for MBTA API", fn -> + Stream.build_options(path: "/vehicles") + end + end + + test "throws error if mbta api key is missing" do + Application.put_env(:dotcom, :mbta_api, base_url: "http://example.com", key: nil) + + assert_raise ArgumentError, "Missing required configuration for MBTA API", fn -> + Stream.build_options(path: "/vehicles") + end + end + end + describe "start_link" do @tag :external test "handles api events" do diff --git a/test/predictions/stream_supervisor_test.exs b/test/predictions/stream_supervisor_test.exs index a5aae988dd..a446984041 100644 --- a/test/predictions/stream_supervisor_test.exs +++ b/test/predictions/stream_supervisor_test.exs @@ -18,6 +18,12 @@ defmodule Predictions.StreamSupervisorTest do :ok end + setup do + Application.put_env(:dotcom, :mbta_api, base_url: "foo", key: "bar") + end + + setup :close_active_workers + defp close_active_workers(context) do StreamSupervisor |> DynamicSupervisor.which_children() @@ -26,8 +32,6 @@ defmodule Predictions.StreamSupervisorTest do context end - setup :close_active_workers - describe "start_link/1" do test "StreamSupervisor is started along with registry" do assert {:error, {:already_started, _}} = StreamSupervisor.start_link([]) From ed1f8b79cb7d92f10c96122c77287d0a6f4f1042 Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Tue, 23 Apr 2024 12:34:06 -0500 Subject: [PATCH 14/19] change monitor env var --- integration/health_checks/v3-api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/health_checks/v3-api.js b/integration/health_checks/v3-api.js index fb8c9c83fc..fa28f978d3 100644 --- a/integration/health_checks/v3-api.js +++ b/integration/health_checks/v3-api.js @@ -1,7 +1,7 @@ const { status200 } = require("../utils"); const options = { - baseURL: process.env.V3_URL, + baseURL: process.env.MBTA_API_BASE_URL, url: "/status", }; From 93d7bdc179e1ff26b7fc77749787e06b16420e4c Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Tue, 23 Apr 2024 12:36:44 -0500 Subject: [PATCH 15/19] rename file --- integration/health_checks/{v3-api.js => mbta-api.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename integration/health_checks/{v3-api.js => mbta-api.js} (100%) diff --git a/integration/health_checks/v3-api.js b/integration/health_checks/mbta-api.js similarity index 100% rename from integration/health_checks/v3-api.js rename to integration/health_checks/mbta-api.js From 8f06d9fa4a3e9421cffb5880a6d2caa136f8c133 Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Tue, 23 Apr 2024 12:43:11 -0500 Subject: [PATCH 16/19] update docs with new env vars --- .env.template | 6 +++--- .github/workflows/algolia-update.yml | 4 ++-- .github/workflows/tests.yml | 4 ++-- README.md | 4 ++-- docs/ENVIRONMENT.md | 5 ++--- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.env.template b/.env.template index e3688ad25a..990893d94c 100644 --- a/.env.template +++ b/.env.template @@ -9,9 +9,9 @@ OPEN_TRIP_PLANNER_URL=http://otp2-local.mbtace.com RECAPTCHA_PUBLIC_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI RECAPTCHA_PRIVATE_KEY=6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe -# V3_API_KEY= -V3_API_VERSION=2019-07-01 -V3_URL=https://api-dev.mbtace.com +# MBTA_API_KEY= +MBTA_API_VERSION=2019-07-01 +MBTA_API_BASE_URL=https://api-dev.mbtace.com # You can optionally set a Redis host and port. # The default host is 127.0.0.1 diff --git a/.github/workflows/algolia-update.yml b/.github/workflows/algolia-update.yml index b8bb8f8b7a..c6d2bd6961 100644 --- a/.github/workflows/algolia-update.yml +++ b/.github/workflows/algolia-update.yml @@ -9,8 +9,8 @@ on: env: MIX_ENV: dev USE_SERVER_SENT_EVENTS: "false" - V3_URL: ${{ secrets.V3_URL }} - V3_API_KEY: ${{ secrets.V3_API_KEY }} + MBTA_API_BASE_URL: ${{ secrets.MBTA_API_BASE_URL }} + MBTA_API_KEY: ${{ secrets.MBTA_API_KEY }} WARM_CACHES: "false" jobs: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 45d0581559..ea4cca355f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,8 +15,8 @@ concurrency: env: MIX_ENV: test - V3_URL: ${{ secrets.V3_URL }} - V3_API_KEY: ${{ secrets.V3_API_KEY }} + MBTA_API_BASE_URLL: ${{ secrets.MBTA_API_BASE_URL }} + MBTA_API_KEY: ${{ secrets.MBTA_API_KEY }} jobs: # Report file changes by extensions diff --git a/README.md b/README.md index 9390f50c76..ff7fa47e1d 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,8 @@ From a development standpoint, polyfills and code transforms are implemented via - [Getting Started](#getting-started) - [Running the Server](#running-the-server) - [Environment Variables](docs/ENVIRONMENT.md) - - [`V3_URL`](docs/ENVIRONMENT.md#v3_url) - - [`V3_API_KEY`](docs/ENVIRONMENT.md#v3_api_key) + - [`MBTA_API_BASE_URL`](docs/ENVIRONMENT.md#v3_url) + - [`MBTA_API_KEY`](docs/ENVIRONMENT.md#v3_api_key) - [`DRUPAL_ROOT`](docs/ENVIRONMENT.md#drupal_root) - [`ALGOLIA_APP_ID`, `ALGOLIA_SEARCH_KEY`, and `ALGOLIA_WRITE_KEY`](docs/ENVIRONMENT.md#algolia_app_id-algolia_search_key-and-algolia_write_key) - [Additional documentation](#additional-resources) diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index e8dd482312..83708f0ec2 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -21,16 +21,15 @@ There are many ways to set the environment variables described here: ## Required -### `V3_URL` +### `MBTA_API_BASE_URL` The URL of the MBTA V3 API server, e.g. `https://api-dev.mbtace.com`. -### `V3_API_KEY` +### `MBTA_API_KEY` The key to use with the MBTA API (see `README`). This is a practical requirement for development since requests without an API key have a very low rate limit. - ## Optional ### `DRUPAL_ROOT` From e7d504d4ef24315d292ea8a4abfdbac6a731a352 Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Wed, 24 Apr 2024 08:46:57 -0500 Subject: [PATCH 17/19] typo --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ea4cca355f..a59ca9e042 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ concurrency: env: MIX_ENV: test - MBTA_API_BASE_URLL: ${{ secrets.MBTA_API_BASE_URL }} + MBTA_API_BASE_URL: ${{ secrets.MBTA_API_BASE_URL }} MBTA_API_KEY: ${{ secrets.MBTA_API_KEY }} jobs: From ae919393f6d239770f67977f546b9913f6f04f33 Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Wed, 24 Apr 2024 10:55:28 -0500 Subject: [PATCH 18/19] correct link in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ff7fa47e1d..0d3ad46d66 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,8 @@ From a development standpoint, polyfills and code transforms are implemented via - [Getting Started](#getting-started) - [Running the Server](#running-the-server) - [Environment Variables](docs/ENVIRONMENT.md) - - [`MBTA_API_BASE_URL`](docs/ENVIRONMENT.md#v3_url) - - [`MBTA_API_KEY`](docs/ENVIRONMENT.md#v3_api_key) + - [`MBTA_API_BASE_URL`](docs/ENVIRONMENT.md#mbta_api_base_url) + - [`MBTA_API_KEY`](docs/ENVIRONMENT.md#mbta_api_key) - [`DRUPAL_ROOT`](docs/ENVIRONMENT.md#drupal_root) - [`ALGOLIA_APP_ID`, `ALGOLIA_SEARCH_KEY`, and `ALGOLIA_WRITE_KEY`](docs/ENVIRONMENT.md#algolia_app_id-algolia_search_key-and-algolia_write_key) - [Additional documentation](#additional-resources) From 521f342b2a9f498e65ba4ef2a2d370036f8a90c5 Mon Sep 17 00:00:00 2001 From: Anthony Shull Date: Wed, 24 Apr 2024 17:45:15 -0500 Subject: [PATCH 19/19] trigger rerun