From e0cc68859e45c2cb71336fc3566524ce1364689f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:31:09 +0000 Subject: [PATCH 1/9] chore(deps-dev): bump @types/lodash in /assets in the npm-patches group Bumps the npm-patches group in /assets with 1 update: [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash). Updates `@types/lodash` from 4.17.10 to 4.17.12 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) --- updated-dependencies: - dependency-name: "@types/lodash" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-patches ... Signed-off-by: dependabot[bot] --- assets/package-lock.json | 8 ++++---- assets/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/package-lock.json b/assets/package-lock.json index 1f529ba5..ca66fe62 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -35,7 +35,7 @@ "@testing-library/react": "^16.0.1", "@types/date-fns": "^2.6.0", "@types/jest": "^29.5.13", - "@types/lodash": "^4.17.10", + "@types/lodash": "^4.17.12", "@types/phoenix": "^1.6.5", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.1", @@ -3381,9 +3381,9 @@ "license": "MIT" }, "node_modules/@types/lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", + "version": "4.17.12", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.12.tgz", + "integrity": "sha512-sviUmCE8AYdaF/KIHLDJBQgeYzPBI0vf/17NaYehBJfYD1j6/L95Slh07NlyK2iNyBNaEkb3En2jRt+a8y3xZQ==", "dev": true }, "node_modules/@types/node": { diff --git a/assets/package.json b/assets/package.json index 1578dfdc..eae7ebe0 100644 --- a/assets/package.json +++ b/assets/package.json @@ -42,7 +42,7 @@ "@testing-library/react": "^16.0.1", "@types/date-fns": "^2.6.0", "@types/jest": "^29.5.13", - "@types/lodash": "^4.17.10", + "@types/lodash": "^4.17.12", "@types/phoenix": "^1.6.5", "@types/react": "^18.3.11", "@types/react-dom": "^18.3.1", From d7e7221ef1e8399e9b4e5440f3bec442b10d6458 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:11:28 +0000 Subject: [PATCH 2/9] chore(deps): bump nebulex from 2.6.3 to 2.6.4 in the mix-patches group Bumps the mix-patches group with 1 update: [nebulex](https://github.com/cabol/nebulex). Updates `nebulex` from 2.6.3 to 2.6.4 - [Release notes](https://github.com/cabol/nebulex/releases) - [Changelog](https://github.com/cabol/nebulex/blob/master/CHANGELOG.md) - [Commits](https://github.com/cabol/nebulex/compare/v2.6.3...v2.6.4) --- updated-dependencies: - dependency-name: nebulex dependency-type: direct:production update-type: version-update:semver-patch dependency-group: mix-patches ... Signed-off-by: dependabot[bot] --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 903afaf2..6910792e 100644 --- a/mix.lock +++ b/mix.lock @@ -34,7 +34,7 @@ "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, - "nebulex": {:hex, :nebulex, "2.6.3", "78af348ed9f8a338871b41e0b6de718c1808e627ce03fbe86598cbda2bdda2f5", [: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", "09cdcbb62f8463ffcec7cae4936425ce91e25d92a6cd37e48b5dda7c851958d5"}, + "nebulex": {:hex, :nebulex, "2.6.4", "4b00706e0e676474783d988962abf74614480e13c0a32645acb89bb32b660e09", [: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", "25bdabf3fb86035c8151bba60bda20f80f96ae0261db7bd4090878ff63b03581"}, "nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.1", "99d5244672fafdfac89bfad3d3ab8f0d367603ce1dc4855f86a1c75008bce56f", [:mix], [], "hexpm", "4bf510adedff0449a1d6e200e43e57a814794c8b5b6439071274d248d272a549"}, "oidcc": {:hex, :oidcc, "3.2.4", "0bbe0f6059682dcb3bb70f682ede606779ea7b73337803a05c7d8eb3ae76acc0", [:mix, :rebar3], [{:jose, "~> 1.11", [hex: :jose, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_registry, "~> 0.3.1", [hex: :telemetry_registry, repo: "hexpm", optional: false]}], "hexpm", "984956348f6f833577b7a6cb72b325936cab3fd1c9cf28d7d54773d3ea48a20a"}, From d987156bd011cad2a29beec13a05c5f87ddaafda Mon Sep 17 00:00:00 2001 From: Christian Maddox Date: Tue, 22 Oct 2024 10:07:34 -0400 Subject: [PATCH 3/9] chore: Fake Watts Client (#536) * Move module to separate folder. * Use fake client for local dev. * Don't decode file. --- config/dev.exs | 3 +- lib/screenplay/{watts.ex => watts/client.ex} | 43 +++++++++++++++--- .../controllers/pa_messages_api_controller.ex | 4 +- priv/static.mp3 | Bin 0 -> 26063 bytes 4 files changed, 42 insertions(+), 8 deletions(-) rename lib/screenplay/{watts.ex => watts/client.ex} (57%) create mode 100644 priv/static.mp3 diff --git a/config/dev.exs b/config/dev.exs index e524e8a2..2309bdea 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -86,7 +86,8 @@ config :screenplay, local_paess_labels_file_spec: {:priv, "paess_labels.json"}, api_v3_key: System.get_env("API_V3_KEY"), github_api_client: Screenplay.GithubApi.FakeClient, - local_signs_json_path: "../realtime_signs/priv/signs.json" + local_signs_json_path: "../realtime_signs/priv/signs.json", + watts_client: Screenplay.Watts.FakeClient config :ueberauth, Ueberauth, providers: [ diff --git a/lib/screenplay/watts.ex b/lib/screenplay/watts/client.ex similarity index 57% rename from lib/screenplay/watts.ex rename to lib/screenplay/watts/client.ex index 4af0495b..12a7ece7 100644 --- a/lib/screenplay/watts.ex +++ b/lib/screenplay/watts/client.ex @@ -1,14 +1,22 @@ -defmodule Screenplay.Watts do +defmodule Screenplay.Watts.ClientBehaviour do + @moduledoc false + + @doc """ + Fetches an audio file from Watts given a string. + """ + @callback fetch_tts(String.t()) :: {:ok, binary()} | :error +end + +defmodule Screenplay.Watts.Client do @moduledoc """ - Module used to fetch TTS audio files from the Watts app. + Client used to fetch TTS audio files from the Watts app. """ require Logger - @doc """ - Fetches an audio file from Watts given a string. - """ - @spec fetch_tts(String.t()) :: :error | {:ok, binary()} + @behaviour Screenplay.Watts.ClientBehaviour + + @impl true def fetch_tts(text) do watts_url = Application.fetch_env!(:screenplay, :watts_url) watts_api_key = Application.fetch_env!(:screenplay, :watts_api_key) @@ -41,3 +49,26 @@ defmodule Screenplay.Watts do :error end end + +defmodule Screenplay.Watts.FakeClient do + @moduledoc false + + require Logger + + @behaviour Screenplay.Watts.ClientBehaviour + + @impl true + # sobelow_skip ["Traversal.FileModule"] + def fetch_tts(_) do + path = Path.join(:code.priv_dir(:screenplay), "static.mp3") + + case File.read(path) do + {:ok, file} -> + {:ok, file} + + {:error, _} -> + Logger.error("Could not fetch static.mp3.") + :error + end + end +end diff --git a/lib/screenplay_web/controllers/pa_messages_api_controller.ex b/lib/screenplay_web/controllers/pa_messages_api_controller.ex index aec7f0c1..0abff059 100644 --- a/lib/screenplay_web/controllers/pa_messages_api_controller.ex +++ b/lib/screenplay_web/controllers/pa_messages_api_controller.ex @@ -6,12 +6,14 @@ defmodule ScreenplayWeb.PaMessagesApiController do alias Screenplay.PaMessages alias Screenplay.PaMessages.ListParams + @watts_client Application.compile_env(:screenplay, :watts_client, Screenplay.Watts.Client) + def active(conn, _params) do json(conn, PaMessages.get_active_messages()) end def preview_audio(conn, %{"text" => text}) do - case Screenplay.Watts.fetch_tts(text) do + case @watts_client.fetch_tts(text) do {:ok, audio_data} -> send_download(conn, {:binary, audio_data}, filename: "preview.mp3") diff --git a/priv/static.mp3 b/priv/static.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..73ca54ccc0b8f51bb8d8dae3d7a2f8e25bd5a2b4 GIT binary patch literal 26063 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 z�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 JBmO_W{69r!l|ujk literal 0 HcmV?d00001 From 653c29996fa00f1740f33fd6799227af4a03d41e Mon Sep 17 00:00:00 2001 From: Cora Grant Date: Fri, 18 Oct 2024 12:35:11 -0400 Subject: [PATCH 4/9] dev: fix test env not respecting DB config The `test` Mix environment used a hard-coded database username, password, and hostname instead of using the values configured in `DATABASE_USER`, etc. as the `dev` environment does. --- config/test.exs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/config/test.exs b/config/test.exs index 72b8a78b..8c10a45b 100644 --- a/config/test.exs +++ b/config/test.exs @@ -38,13 +38,7 @@ config :ueberauth_oidcc, ] ] -config :screenplay, Screenplay.Repo, - adapter: Ecto.Adapters.Postgres, - database: "screenplay_test", - pool: Ecto.Adapters.SQL.Sandbox, - username: "postgres", - password: "postgres", - hostname: "localhost" +config :screenplay, Screenplay.Repo, database: "screenplay_test", pool: Ecto.Adapters.SQL.Sandbox # Print only warnings and errors during test config :logger, level: :warning From 1a8951d03d395301673710c21f2682538277fbdc Mon Sep 17 00:00:00 2001 From: Cora Grant Date: Fri, 18 Oct 2024 12:36:46 -0400 Subject: [PATCH 5/9] test: fix invalid vendor value in fixture `screens-config-lib` now enforces that the `vendor` field is one of the recognized values. --- test/fixtures/builder/screens_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/builder/screens_config.json b/test/fixtures/builder/screens_config.json index bca56dbc..2259950e 100644 --- a/test/fixtures/builder/screens_config.json +++ b/test/fixtures/builder/screens_config.json @@ -345,7 +345,7 @@ "medium_asset_path": "images/bus-shelter/survey/10413-weld-sb-medium.png" } }, - "vendor": "lg-mri", + "vendor": "lg_mri", "device_id": "TEST", "hidden_from_screenplay": false }, From 597df32782c81e905d13db4ed20d849aa9848e63 Mon Sep 17 00:00:00 2001 From: Cora Grant Date: Fri, 18 Oct 2024 12:38:00 -0400 Subject: [PATCH 6/9] chore: remove audio configuration We are removing the ability to configure audio per-screen for screen types that don't need it, which includes GL E-ink. These fields were never exposed in Permanent Config anyway, and were always initialized to the same default value. --- lib/screenplay/permanent_config.ex | 11 +----- mix.exs | 2 +- mix.lock | 2 +- test/fixtures/builder/screens_config.json | 30 ++-------------- test/screenplay/permanent_config_test.exs | 42 +---------------------- 5 files changed, 6 insertions(+), 81 deletions(-) diff --git a/lib/screenplay/permanent_config.ex b/lib/screenplay/permanent_config.ex index 77221891..d67e169a 100644 --- a/lib/screenplay/permanent_config.ex +++ b/lib/screenplay/permanent_config.ex @@ -12,7 +12,7 @@ defmodule Screenplay.PermanentConfig do alias Screenplay.ScreensConfig, as: ScreensConfigStore alias Screenplay.ScreensConfig.Fetch, as: PublishedScreensFetch alias ScreensConfig.{Config, PendingConfig, Screen} - alias ScreensConfig.V2.{Alerts, Audio, Departures, Footer, GlEink, LineMap} + alias ScreensConfig.V2.{Alerts, Departures, Footer, GlEink, LineMap} alias ScreensConfig.V2.Departures.{Query, Section} alias ScreensConfig.V2.Header.Destination @@ -290,7 +290,6 @@ defmodule Screenplay.PermanentConfig do app_id: :gl_eink_v2, app_params: struct(GlEink, - audio: default_enabled_audio_config(), departures: struct(Departures, sections: [ @@ -364,14 +363,6 @@ defmodule Screenplay.PermanentConfig do end) end - defp default_enabled_audio_config do - %Audio{ - start_time: ~T[00:00:00], - stop_time: ~T[23:59:59], - days_active: [1, 2, 3, 4, 5, 6, 7] - } - end - # Each screen type will look in a different part of the configuration to find it's physical location defp get_route_id(:gl_eink_v2, updated_pending_screens, new_pending_screens) do updated_pending_screens diff --git a/mix.exs b/mix.exs index d610a346..231b08dd 100644 --- a/mix.exs +++ b/mix.exs @@ -64,7 +64,7 @@ defmodule Screenplay.MixProject do {:postgrex, ">= 0.0.0"}, {:screens_config, git: "https://github.com/mbta/screens-config-lib.git", - ref: "594c88ae0a4e9deb43697ab5e0567f1c97f19671"}, + ref: "8ec6e1684a129b089edc5e867a32dfc90028b2e0"}, {:mox, "~> 1.0", only: :test}, {:quantum, "~> 3.0"}, {:tzdata, "~> 1.1"}, diff --git a/mix.lock b/mix.lock index 6910792e..1e0ef722 100644 --- a/mix.lock +++ b/mix.lock @@ -54,7 +54,7 @@ "quantum": {:hex, :quantum, "3.5.3", "ee38838a07761663468145f489ad93e16a79440bebd7c0f90dc1ec9850776d99", [:mix], [{:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.14 or ~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_registry, "~> 0.2", [hex: :telemetry_registry, repo: "hexpm", optional: false]}], "hexpm", "500fd3fa77dcd723ed9f766d4a175b684919ff7b6b8cfd9d7d0564d58eba8734"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, - "screens_config": {:git, "https://github.com/mbta/screens-config-lib.git", "594c88ae0a4e9deb43697ab5e0567f1c97f19671", [ref: "594c88ae0a4e9deb43697ab5e0567f1c97f19671"]}, + "screens_config": {:git, "https://github.com/mbta/screens-config-lib.git", "8ec6e1684a129b089edc5e867a32dfc90028b2e0", [ref: "8ec6e1684a129b089edc5e867a32dfc90028b2e0"]}, "sentry": {:hex, :sentry, "10.4.0", "d8ffe8ce15b4b53f5e879299c3c222324c289a47a507c0b251c4f91ce7bae9ff", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "e5f98892152879dc87363b1b7f774eeddb8cf7dddfa7355e40eba188b2cae58a"}, "sftp_client": {:hex, :sftp_client, "2.0.1", "0611d7e4cdb4abb04627dd259c3d77fddec76c68462582abf001a284e030bc52", [:mix], [], "hexpm", "0b0b47071b0a44a898c9c508d307a8e77534f7de9348707efbf7c88c7f33f010"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, diff --git a/test/fixtures/builder/screens_config.json b/test/fixtures/builder/screens_config.json index 2259950e..254cb072 100644 --- a/test/fixtures/builder/screens_config.json +++ b/test/fixtures/builder/screens_config.json @@ -12,16 +12,6 @@ "alerts": { "stop_id": "70232" }, - "audio": { - "start_time": "00:00:00", - "days_active": [1, 2, 3, 4, 5, 6, 7], - "daytime_start_time": "00:00:00", - "daytime_stop_time": "23:59:59", - "daytime_volume": 1, - "interval_offset_seconds": 0, - "nighttime_volume": 1, - "stop_time": "23:59:59" - }, "departures": { "sections": [ { @@ -210,14 +200,8 @@ "stop_id": "10413" }, "audio": { - "start_time": "04:45:00", - "days_active": [1, 2, 3, 4, 5, 6, 7], - "daytime_start_time": "07:00:00", - "daytime_stop_time": "21:00:00", - "daytime_volume": 1, - "interval_offset_seconds": 150, - "nighttime_volume": 0.5, - "stop_time": "01:45:00" + "interval_enabled": true, + "interval_offset_seconds": 150 }, "departures": { "sections": [ @@ -359,16 +343,6 @@ "header": { "stop_name": "Haymarket" }, - "audio": { - "start_time": "00:00:00", - "days_active": [1, 2, 3, 4, 5, 6, 7], - "daytime_start_time": "00:00:00", - "daytime_stop_time": "00:00:00", - "daytime_volume": 0, - "interval_offset_seconds": 0, - "nighttime_volume": 0, - "stop_time": "23:59:59" - }, "departures": { "sections": [ { diff --git a/test/screenplay/permanent_config_test.exs b/test/screenplay/permanent_config_test.exs index 6fedb0e1..d3e668a6 100644 --- a/test/screenplay/permanent_config_test.exs +++ b/test/screenplay/permanent_config_test.exs @@ -8,7 +8,7 @@ defmodule Screenplay.PermanentConfigTest do alias Screenplay.Places.{Cache, Place} alias Screenplay.Places.Place.ShowtimeScreen alias ScreensConfig.{PendingConfig, Screen} - alias ScreensConfig.V2.{Alerts, Audio, Departures, Footer, GlEink, Header, LineMap} + alias ScreensConfig.V2.{Alerts, Departures, Footer, GlEink, Header, LineMap} def fetch_current_config_version do {:ok, _config, metadata} = Local.fetch_config() @@ -125,16 +125,6 @@ defmodule Screenplay.PermanentConfigTest do route_id: "Green-B" }, evergreen_content: [], - audio: %Audio{ - start_time: ~T[00:00:00], - stop_time: ~T[23:59:59], - daytime_start_time: ~T[00:00:00], - daytime_stop_time: ~T[00:00:00], - days_active: [1, 2, 3, 4, 5, 6, 7], - daytime_volume: 0.0, - nighttime_volume: 0.0, - interval_offset_seconds: 0 - }, platform_location: "front" }, tags: [] @@ -207,16 +197,6 @@ defmodule Screenplay.PermanentConfigTest do route_id: "Green-B" }, evergreen_content: [], - audio: %Audio{ - start_time: ~T[00:00:00], - stop_time: ~T[23:59:59], - daytime_start_time: ~T[00:00:00], - daytime_stop_time: ~T[00:00:00], - days_active: [1, 2, 3, 4, 5, 6, 7], - daytime_volume: 0.0, - nighttime_volume: 0.0, - interval_offset_seconds: 0 - }, platform_location: "back" }, tags: [] @@ -355,16 +335,6 @@ defmodule Screenplay.PermanentConfigTest do route_id: "Green-B" }, evergreen_content: [], - audio: %Audio{ - start_time: ~T[00:00:00], - stop_time: ~T[23:59:59], - daytime_start_time: ~T[00:00:00], - daytime_stop_time: ~T[00:00:00], - days_active: [1, 2, 3, 4, 5, 6, 7], - daytime_volume: 0.0, - nighttime_volume: 0.0, - interval_offset_seconds: 0 - }, platform_location: "back" }, tags: [] @@ -404,16 +374,6 @@ defmodule Screenplay.PermanentConfigTest do route_id: "Green-B" }, evergreen_content: [], - audio: %Audio{ - start_time: ~T[00:00:00], - stop_time: ~T[23:59:59], - daytime_start_time: ~T[00:00:00], - daytime_stop_time: ~T[00:00:00], - days_active: [1, 2, 3, 4, 5, 6, 7], - daytime_volume: 0.0, - nighttime_volume: 0.0, - interval_offset_seconds: 0 - }, platform_location: "back" }, tags: [] From 978c86cfe1686f078a3c2bd281d1e72fa553c8d2 Mon Sep 17 00:00:00 2001 From: Christian Maddox Date: Fri, 25 Oct 2024 11:22:37 -0400 Subject: [PATCH 7/9] feat: Elevator Screens (#529) * Update screens-config-lib. * Updated client to use new elevator app ID. * Get stop from facility for elevator screens. * Fix outdated vendor in fixture and removed unnecessary put_env. * Added elevator screens to test. * Safely extract stop_id from data. * Allow inline screens to wrap. * Group inline screens by type. * Switch to lodash/fp. --- assets/css/screen-detail.scss | 2 ++ assets/css/screen-simulation.scss | 3 ++- assets/js/components/Dashboard/PlaceRow.tsx | 1 + .../Dashboard/PlaceRowAccordion.tsx | 16 ++++++++--- assets/js/constants/constants.ts | 2 +- assets/js/util.ts | 1 + config/test.exs | 3 ++- lib/screenplay/facilities/facility.ex | 15 +++++++++++ lib/screenplay/places/builder.ex | 12 +++++++++ mix.lock | 16 +++++------ test/fixtures/builder/screens_config.json | 14 ++++++++++ test/screenplay/places/builder_test.exs | 27 +++++++++++++++++++ test/support/mocks.ex | 1 + test/test_helper.exs | 2 -- 14 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 lib/screenplay/facilities/facility.ex diff --git a/assets/css/screen-detail.scss b/assets/css/screen-detail.scss index ac45145a..95f14123 100644 --- a/assets/css/screen-detail.scss +++ b/assets/css/screen-detail.scss @@ -1,6 +1,8 @@ .screen-detail__inline-layout { display: flex; justify-content: space-between; + flex-wrap: wrap; + gap: 4px; } .screen-detail__container { diff --git a/assets/css/screen-simulation.scss b/assets/css/screen-simulation.scss index e5ece02b..77c379e6 100644 --- a/assets/css/screen-simulation.scss +++ b/assets/css/screen-simulation.scss @@ -45,7 +45,8 @@ width: 100%; &--busway_v2, - &--bus_shelter_v2 { + &--bus_shelter_v2, + &--elevator_v2 { height: 379px; } diff --git a/assets/js/components/Dashboard/PlaceRow.tsx b/assets/js/components/Dashboard/PlaceRow.tsx index 9fd48b4d..c1b23a4e 100644 --- a/assets/js/components/Dashboard/PlaceRow.tsx +++ b/assets/js/components/Dashboard/PlaceRow.tsx @@ -31,6 +31,7 @@ const typeMap: Record = { bus_eink_v2: "Bus E-Ink", solari: "Sectional", busway_v2: "Sectional", + elevator_v2: "Elevator", }; const inlineMap = (place: Place, line: string) => { diff --git a/assets/js/components/Dashboard/PlaceRowAccordion.tsx b/assets/js/components/Dashboard/PlaceRowAccordion.tsx index 4f667d47..173f2113 100644 --- a/assets/js/components/Dashboard/PlaceRowAccordion.tsx +++ b/assets/js/components/Dashboard/PlaceRowAccordion.tsx @@ -15,6 +15,7 @@ import ScreenDetail from "Components/ScreenDetail"; import { sortScreens } from "../../util"; import { useUpdateAnimation } from "Hooks/useUpdateAnimation"; import classNames from "classnames"; +import fp from "lodash/fp"; type ScreenGroup = { screens: Screen[]; @@ -22,12 +23,14 @@ type ScreenGroup = { }; const groupScreens = (screens: Screen[]): ScreenGroup[] => { + const inlineScreenTypes = ["busway_v2", "solari", "elevator_v2"]; + const inlineScreens = screens.filter((screen) => - ["busway_v2", "solari"].includes(screen.type), + inlineScreenTypes.includes(screen.type), ); const paEssScreens = screens.filter((screen) => screen.type === "pa_ess"); const otherScreens = screens.filter( - (screen) => !["busway_v2", "pa_ess", "solari"].includes(screen.type), + (screen) => ![...inlineScreenTypes, "pa_ess"].includes(screen.type), ); const groups = otherScreens.map((screen) => ({ @@ -36,7 +39,14 @@ const groupScreens = (screens: Screen[]): ScreenGroup[] => { })); if (inlineScreens.length > 0) { - groups.push({ screens: inlineScreens, isInline: true }); + const groupedInlineScreens: Screen[][] = fp.flow( + fp.groupBy((screen: Screen) => screen.type), + fp.map((screens) => screens), + )(inlineScreens); + + groupedInlineScreens.forEach((screens) => + groups.push({ screens: screens, isInline: true }), + ); } if (paEssScreens.length > 0) { diff --git a/assets/js/constants/constants.ts b/assets/js/constants/constants.ts index d9b12d2f..9a54d2e1 100644 --- a/assets/js/constants/constants.ts +++ b/assets/js/constants/constants.ts @@ -50,7 +50,7 @@ export const SCREEN_TYPES: { label: string; ids: string[] }[] = [ label: "E-Ink: Green Line", ids: ["gl_eink_single", "gl_eink_double", "gl_eink_v2"], }, - { label: "Elevator", ids: ["elevator"] }, + { label: "Elevator", ids: ["elevator_v2"] }, { label: "PA ESS", ids: ["pa_ess"] }, { label: "Pre Fare Duo", ids: ["pre_fare_v2"] }, { label: "Sectional", ids: ["busway_v2", "solari"] }, diff --git a/assets/js/util.ts b/assets/js/util.ts index cda460da..26abddff 100644 --- a/assets/js/util.ts +++ b/assets/js/util.ts @@ -212,6 +212,7 @@ const screenTypeOrder = [ "gl_eink_double", "gl_eink_v2", "pre_fare_v2", + "elevator_v2", "pa_ess", ]; diff --git a/config/test.exs b/config/test.exs index 8c10a45b..10cb3eb2 100644 --- a/config/test.exs +++ b/config/test.exs @@ -22,7 +22,8 @@ config :screenplay, github_api_client: Screenplay.GithubApi.FakeClient, local_signs_json_path: {:test, "signs.json"}, stops_mod: Screenplay.Stops.Mock, - routes_mod: Screenplay.Routes.Mock + routes_mod: Screenplay.Routes.Mock, + facilities_mod: Screenplay.Facilities.Mock config :ueberauth, Ueberauth, providers: [ diff --git a/lib/screenplay/facilities/facility.ex b/lib/screenplay/facilities/facility.ex new file mode 100644 index 00000000..b6649291 --- /dev/null +++ b/lib/screenplay/facilities/facility.ex @@ -0,0 +1,15 @@ +defmodule Screenplay.Facilities.Facility do + @moduledoc """ + Functions used to fetch facility data from the V3 API. + """ + + alias Screenplay.V3Api + + @callback fetch(String.t()) :: {:ok, map()} | :error + def fetch(facility_id) do + case V3Api.get_json("/facilities/#{facility_id}") do + {:ok, %{"data" => data}} -> {:ok, data} + _ -> :error + end + end +end diff --git a/lib/screenplay/places/builder.ex b/lib/screenplay/places/builder.ex index abbec12f..f0181bb4 100644 --- a/lib/screenplay/places/builder.ex +++ b/lib/screenplay/places/builder.ex @@ -20,6 +20,7 @@ defmodule Screenplay.Places.Builder do Busway, Departures, Dup, + Elevator, Footer, GlEink, Header, @@ -34,6 +35,11 @@ defmodule Screenplay.Places.Builder do @config_fetcher Application.compile_env!(:screenplay, :config_fetcher) @stops_mod Application.compile_env(:screenplay, :stops_mod, Screenplay.Stops.Stop) @routes_mod Application.compile_env(:screenplay, :routes_mod, Screenplay.Routes.Route) + @facilities_mod Application.compile_env( + :screenplay, + :facilities_mod, + Screenplay.Facilities.Facility + ) @github_api_client Application.compile_env( :screenplay, :github_api_client, @@ -228,6 +234,12 @@ defmodule Screenplay.Places.Builder do stop_id end + defp get_stop_id({_id, %{app_params: %Elevator{elevator_id: elevator_id}}}) do + {:ok, facility} = @facilities_mod.fetch(elevator_id) + %{"relationships" => %{"stop" => %{"data" => %{"id" => stop_id}}}} = facility + stop_id + end + defp split_multi_place_screens( {id, %Screen{ diff --git a/mix.lock b/mix.lock index 1e0ef722..b127a788 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"}, + "castore": {:hex, :castore, "1.0.9", "5cc77474afadf02c7c017823f460a17daa7908e991b0cc917febc90e466a375c", [:mix], [], "hexpm", "5ea956504f1ba6f2b4eb707061d8e17870de2bee95fb59d512872c2ef06925e7"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, @@ -11,13 +11,13 @@ "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dialyxir": {:hex, :dialyxir, "1.4.4", "fb3ce8741edeaea59c9ae84d5cec75da00fa89fe401c72d6e047d11a61f65f70", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cd6111e8017ccd563e65621a4d9a4a1c5cd333df30cebc7face8029cacb4eff6"}, - "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, - "ecto_sql": {:hex, :ecto_sql, "3.11.3", "4eb7348ff8101fbc4e6bbc5a4404a24fecbe73a3372d16569526b0cf34ebc195", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e5f36e3d736b99c7fee3e631333b8394ade4bafe9d96d35669fca2d81c2be928"}, + "ecto": {:hex, :ecto, "3.12.4", "267c94d9f2969e6acc4dd5e3e3af5b05cdae89a4d549925f3008b2b7eb0b93c3", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ef04e4101688a67d061e1b10d7bc1fbf00d1d13c17eef08b71d070ff9188f747"}, + "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_aws": {:hex, :ex_aws, "2.5.6", "6f642e0f82eff10a9b470044f084b81a791cf15b393d647ea5f3e65da2794e3d", [:mix], [{:configparser_ex, "~> 4.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8 or ~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:req, "~> 0.3", [hex: :req, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c69eec59e31fdd89d0beeb1d97e16518dd1b23ad95b3d5c9f1dcfec23d97f960"}, "ex_aws_rds": {:hex, :ex_aws_rds, "2.0.2", "38dd8e83d57cf4b7286c4f6f5c978f700c40c207ffcdd6ca5d738e5eba933f9a", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}], "hexpm", "9e5b5cc168077874cbd0d29ba65d01caf1877e705fb5cecacf0667dd19bfa75c"}, "ex_aws_s3": {:hex, :ex_aws_s3, "2.5.4", "87aaf4a2f24a48f516d7f5aaced9d128dd5d0f655c4431f9037a11a85c71109c", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "c06e7f68b33f7c0acba1361dbd951c79661a28f85aa2e0582990fccca4425355"}, - "ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"}, + "ex_machina": {:hex, :ex_machina, "2.8.0", "a0e847b5712065055ec3255840e2c78ef9366634d62390839d4880483be38abe", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "79fe1a9c64c0c1c1fab6c4fa5d871682cb90de5885320c187d117004627a7729"}, "expo": {:hex, :expo, "0.5.2", "beba786aab8e3c5431813d7a44b828e7b922bfa431d6bfbada0904535342efe2", [:mix], [], "hexpm", "8c9bfa06ca017c9cb4020fabe980bc7fdb1aaec059fd004c2ab3bff03b1c599c"}, "faker": {:hex, :faker, "0.18.0", "943e479319a22ea4e8e39e8e076b81c02827d9302f3d32726c5bf82f430e6e14", [:mix], [], "hexpm", "bfbdd83958d78e2788e99ec9317c4816e651ad05e24cfd1196ce5db5b3e81797"}, "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, @@ -35,8 +35,8 @@ "mimerl": {:hex, :mimerl, "1.3.0", "d0cd9fc04b9061f82490f6581e0128379830e78535e017f7780f37fea7545726", [:rebar3], [], "hexpm", "a1e15a50d1887217de95f0b9b0793e32853f7c258a5cd227650889b38839fe9d"}, "mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"}, "nebulex": {:hex, :nebulex, "2.6.4", "4b00706e0e676474783d988962abf74614480e13c0a32645acb89bb32b660e09", [: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", "25bdabf3fb86035c8151bba60bda20f80f96ae0261db7bd4090878ff63b03581"}, - "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_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, + "nimble_ownership": {:hex, :nimble_ownership, "0.3.2", "d4fa4056ade0ae33b5a9eb64554a1b3779689282e37513260125d2d6b32e4874", [:mix], [], "hexpm", "28b9a9f4094fda1aa8ca72f732ff3223eb54aa3eda4fed9022254de2c152b138"}, "oidcc": {:hex, :oidcc, "3.2.4", "0bbe0f6059682dcb3bb70f682ede606779ea7b73337803a05c7d8eb3ae76acc0", [:mix, :rebar3], [{:jose, "~> 1.11", [hex: :jose, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_registry, "~> 0.3.1", [hex: :telemetry_registry, repo: "hexpm", optional: false]}], "hexpm", "984956348f6f833577b7a6cb72b325936cab3fd1c9cf28d7d54773d3ea48a20a"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"}, @@ -56,7 +56,7 @@ "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, "screens_config": {:git, "https://github.com/mbta/screens-config-lib.git", "8ec6e1684a129b089edc5e867a32dfc90028b2e0", [ref: "8ec6e1684a129b089edc5e867a32dfc90028b2e0"]}, "sentry": {:hex, :sentry, "10.4.0", "d8ffe8ce15b4b53f5e879299c3c222324c289a47a507c0b251c4f91ce7bae9ff", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_ownership, "~> 0.3.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "e5f98892152879dc87363b1b7f774eeddb8cf7dddfa7355e40eba188b2cae58a"}, - "sftp_client": {:hex, :sftp_client, "2.0.1", "0611d7e4cdb4abb04627dd259c3d77fddec76c68462582abf001a284e030bc52", [:mix], [], "hexpm", "0b0b47071b0a44a898c9c508d307a8e77534f7de9348707efbf7c88c7f33f010"}, + "sftp_client": {:hex, :sftp_client, "2.1.0", "e1de4a116437427cfc4b5b303968655a18ac837514f5ca03d42184ef1d77e6b5", [:mix], [], "hexpm", "b87a6b85454f14a170d7fdc42e5ce3edd699f2795d0f8c65d4e0b3a2ebf0e326"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "stream_data": {:hex, :stream_data, "1.1.2", "05499eaec0443349ff877aaabc6e194e82bda6799b9ce6aaa1aadac15a9fdb4d", [:mix], [], "hexpm", "129558d2c77cbc1eb2f4747acbbea79e181a5da51108457000020a906813a1a9"}, @@ -70,5 +70,5 @@ "ueberauth_oidcc": {:hex, :ueberauth_oidcc, "0.4.0", "3fbfbc38735b4dba54ed8bf3e9b80f5054f73cc1ec9af6ae88b7886d25934768", [:mix], [{:oidcc, "~> 3.2.0", [hex: :oidcc, repo: "hexpm", optional: false]}, {:plug, "~> 1.11", [hex: :plug, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.10", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm", "cdd8517d773cfe499c0b692f795f213b2eb33119afbec34aefd8be0a85c62b21"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, - "websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"}, } diff --git a/test/fixtures/builder/screens_config.json b/test/fixtures/builder/screens_config.json index 254cb072..5d94dd64 100644 --- a/test/fixtures/builder/screens_config.json +++ b/test/fixtures/builder/screens_config.json @@ -544,6 +544,20 @@ "vendor": "outfront", "device_id": null, "hidden_from_screenplay": true + }, + "ELE-101": { + "disabled": false, + "name": "", + "tags": [], + "app_id": "elevator_v2", + "app_params": { + "evergreen_content": [], + "elevator_id": "842" + }, + "refresh_if_loaded_before": null, + "hidden_from_screenplay": false, + "vendor": "mimo", + "device_id": "" } } } diff --git a/test/screenplay/places/builder_test.exs b/test/screenplay/places/builder_test.exs index 856c14ab..d454f111 100644 --- a/test/screenplay/places/builder_test.exs +++ b/test/screenplay/places/builder_test.exs @@ -37,6 +37,10 @@ defmodule Screenplay.Places.BuilderTest do {:ok, [%{"id" => "Red", "attributes" => %{"type" => 1}}]} end) + expect(Screenplay.Facilities.Mock, :fetch, 1, fn _ -> + {:ok, %{"relationships" => %{"stop" => %{"data" => %{"id" => "place-tapst"}}}}} + end) + assert {:noreply, _} = Builder.handle_info(:build, []) assert [ @@ -45,6 +49,13 @@ defmodule Screenplay.Places.BuilderTest do name: "Tappan Street", routes: ["Red"], screens: [ + %Place.ShowtimeScreen{ + direction_id: nil, + disabled: false, + id: "ELE-101", + location: "", + type: :elevator_v2 + }, %Place.ShowtimeScreen{ id: "EIG-546", type: :gl_eink_v2, @@ -76,6 +87,10 @@ defmodule Screenplay.Places.BuilderTest do {:ok, [%{"id" => "108", "attributes" => %{"type" => 3}}]} end) + expect(Screenplay.Facilities.Mock, :fetch, 1, fn _ -> + {:ok, %{"relationships" => %{"stop" => %{"data" => %{"id" => "place-tapst"}}}}} + end) + assert {:noreply, _} = Builder.handle_info(:build, []) assert [ @@ -132,6 +147,10 @@ defmodule Screenplay.Places.BuilderTest do ]} end) + expect(Screenplay.Facilities.Mock, :fetch, 1, fn _ -> + {:ok, %{"relationships" => %{"stop" => %{"data" => %{"id" => "place-tapst"}}}}} + end) + assert {:noreply, _} = Builder.handle_info(:build, []) assert [ @@ -179,6 +198,10 @@ defmodule Screenplay.Places.BuilderTest do {:ok, [%{"id" => "Blue", "attributes" => %{"type" => 1}}]} end) + expect(Screenplay.Facilities.Mock, :fetch, 1, fn _ -> + {:ok, %{"relationships" => %{"stop" => %{"data" => %{"id" => "place-tapst"}}}}} + end) + assert {:noreply, _} = Builder.handle_info(:build, []) assert [ @@ -241,6 +264,10 @@ defmodule Screenplay.Places.BuilderTest do {:ok, [%{"id" => "Orange", "attributes" => %{"type" => 1}}]} end) + expect(Screenplay.Facilities.Mock, :fetch, 1, fn _ -> + {:ok, %{"relationships" => %{"stop" => %{"data" => %{"id" => "place-tapst"}}}}} + end) + assert {:noreply, _} = Builder.handle_info(:build, []) assert [ diff --git a/test/support/mocks.ex b/test/support/mocks.ex index ce4e6f63..ea4da7eb 100644 --- a/test/support/mocks.ex +++ b/test/support/mocks.ex @@ -1,3 +1,4 @@ Mox.defmock(Screenplay.RoutePatterns.Mock, for: Screenplay.RoutePatterns.Behaviour) Mox.defmock(Screenplay.Stops.Mock, for: Screenplay.Stops.Stop) Mox.defmock(Screenplay.Routes.Mock, for: Screenplay.Routes.Route) +Mox.defmock(Screenplay.Facilities.Mock, for: Screenplay.Facilities.Facility) diff --git a/test/test_helper.exs b/test/test_helper.exs index e09d88c5..257e0a34 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,6 +1,4 @@ Application.put_env(:screenplay, :route_pattern_mod, Screenplay.RoutePatterns.Mock) -Application.put_env(:screenplay, :stops_mod, Screenplay.Stops.Mock) -Application.put_env(:screenplay, :routes_mod, Screenplay.Routes.Mock) Ecto.Adapters.SQL.Sandbox.mode(Screenplay.Repo, :manual) ExUnit.start() From 89faf7c422c14d840c18a4d73a5dbdf7c717561c Mon Sep 17 00:00:00 2001 From: Christian Maddox Date: Fri, 25 Oct 2024 14:47:57 -0400 Subject: [PATCH 8/9] chore: Refactor toast (#538) * Refactor toast so it's not just for errors. * Address PR feedback. --- assets/css/dashboard/error-toast.scss | 38 ----------- assets/css/dashboard/toast.scss | 52 +++++++++++++++ assets/css/screenplay.scss | 2 +- assets/css/variables.scss | 1 + assets/js/components/Dashboard/ErrorToast.tsx | 54 ---------------- .../Dashboard/PaMessageForm/PaMessageForm.tsx | 7 ++- assets/js/components/Dashboard/Toast.tsx | 63 +++++++++++++++++++ 7 files changed, 121 insertions(+), 96 deletions(-) delete mode 100644 assets/css/dashboard/error-toast.scss create mode 100644 assets/css/dashboard/toast.scss delete mode 100644 assets/js/components/Dashboard/ErrorToast.tsx create mode 100644 assets/js/components/Dashboard/Toast.tsx diff --git a/assets/css/dashboard/error-toast.scss b/assets/css/dashboard/error-toast.scss deleted file mode 100644 index 64469feb..00000000 --- a/assets/css/dashboard/error-toast.scss +++ /dev/null @@ -1,38 +0,0 @@ -.error-alert-container { - display: flex; - justify-content: center; - align-items: center; - position: fixed; - left: 50%; - transform: translateX(-50%); - bottom: 40px; - - .error-alert { - width: 400px; - background-color: $alert-yellow; - font-size: 16px; - display: flex; - align-items: center; - - .btn-close { - &:hover { - background-color: transparent; - } - } - - &__icon { - height: 24px; - width: 24px; - opacity: 60%; - flex-shrink: 0; - } - - &__text { - padding-left: 8px; - - ul { - margin-bottom: 0; - } - } - } -} diff --git a/assets/css/dashboard/toast.scss b/assets/css/dashboard/toast.scss new file mode 100644 index 00000000..06cb2ce2 --- /dev/null +++ b/assets/css/dashboard/toast.scss @@ -0,0 +1,52 @@ +.toast-container { + margin-bottom: 40px; + + .toast { + font-size: 16px; + display: flex; + align-items: center; + + .btn-close { + &:hover { + background-color: transparent; + } + } + + &--warning { + background-color: $alert-yellow; + } + + &--info { + background-color: $accessibility-blue; + color: white; + } + + &__icon { + height: 24px; + width: 24px; + opacity: 60%; + flex-shrink: 0; + } + + &__text { + padding-left: 8px; + + ul { + margin-bottom: 0; + } + } + } + + .toast-header { + .btn-close { + margin-right: 4px; + + &:hover { + background-color: transparent; + } + } + + border: none; + border-radius: 4px; + } +} diff --git a/assets/css/screenplay.scss b/assets/css/screenplay.scss index febcdbde..6ddeb692 100644 --- a/assets/css/screenplay.scss +++ b/assets/css/screenplay.scss @@ -43,7 +43,7 @@ $form-feedback-invalid-color: $text-error; @import "sort-label.scss"; @import "search-bar.scss"; @import "dashboard/picker.scss"; -@import "dashboard/error-toast.scss"; +@import "dashboard/toast.scss"; html { font-size: 16px; diff --git a/assets/css/variables.scss b/assets/css/variables.scss index c4d0949a..cb372037 100644 --- a/assets/css/variables.scss +++ b/assets/css/variables.scss @@ -67,3 +67,4 @@ $button-secondary-outline-active-bg: #d9d9d93d; $text-button-blue-hover-color: #c1e4ff14; $text-button-grey-hover-color: #f8f9fa14; $alert-yellow: #ffdd00; +$accessibility-blue: #165c96; diff --git a/assets/js/components/Dashboard/ErrorToast.tsx b/assets/js/components/Dashboard/ErrorToast.tsx deleted file mode 100644 index 1996f306..00000000 --- a/assets/js/components/Dashboard/ErrorToast.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from "react"; -import { Alert } from "react-bootstrap"; -import { ExclamationTriangleFill } from "react-bootstrap-icons"; - -interface Props { - errorMessage: string | null; - errors: string[]; - onClose: () => void; -} - -const ErrorToast = ({ errorMessage, errors, onClose }: Props) => { - const getErrorMessageFromField = (error: string) => { - switch (error) { - case "sign_ids": - return "Add Stations & Zones"; - case "visual_text": - return "Visual Text"; - case "audio_text": - return "Phoentic Audio"; - case "start_datetime": - return "Start date/time"; - case "end_datetime": - return "End date/time"; - default: - return ""; - } - }; - - return ( -
- - -
- {errorMessage} - {errors.length > 0 && ( -
    - {errors.map((error, i) => { - return
  • {getErrorMessageFromField(error)}
  • ; - })} -
- )} -
-
-
- ); -}; - -export default ErrorToast; diff --git a/assets/js/components/Dashboard/PaMessageForm/PaMessageForm.tsx b/assets/js/components/Dashboard/PaMessageForm/PaMessageForm.tsx index 41f01ecc..5bef918f 100644 --- a/assets/js/components/Dashboard/PaMessageForm/PaMessageForm.tsx +++ b/assets/js/components/Dashboard/PaMessageForm/PaMessageForm.tsx @@ -6,7 +6,7 @@ import SelectStationsAndZones from "./SelectStationsAndZones"; import AssociateAlert from "./AssociateAlert"; import { Alert, InformedEntity } from "Models/alert"; import { usePlacesWithPaEss } from "Hooks/usePlacesWithPaEss"; -import ErrorToast from "Components/ErrorToast"; +import Toast from "Components/Toast"; import { busRouteIdsAtPlaces, getRouteIdsForSign } from "../../../util"; import fp from "lodash/fp"; @@ -217,8 +217,9 @@ const PaMessageForm = ({ setEndWithEffectPeriod={setEndWithEffectPeriod} /> )} - { onErrorsChange([]); diff --git a/assets/js/components/Dashboard/Toast.tsx b/assets/js/components/Dashboard/Toast.tsx new file mode 100644 index 00000000..3db3fa6d --- /dev/null +++ b/assets/js/components/Dashboard/Toast.tsx @@ -0,0 +1,63 @@ +import React from "react"; +import { Toast as BSToast, ToastContainer } from "react-bootstrap"; +import { + CheckCircleFill, + ExclamationTriangleFill, +} from "react-bootstrap-icons"; +import { classWithModifier } from "../../util"; + +interface ToastProps { + variant: "warning" | "info"; + message: string | null; + errors?: string[]; + onClose?: () => void; +} + +const Toast = ({ message, errors = [], onClose, variant }: ToastProps) => { + const getErrorMessageFromField = (error: string) => { + switch (error) { + case "sign_ids": + return "Add Stations & Zones"; + case "visual_text": + return "Visual Text"; + case "audio_text": + return "Phonetic Audio"; + case "start_datetime": + return "Start date/time"; + case "end_datetime": + return "End date/time"; + default: + return ""; + } + }; + + const Icon = + variant === "warning" ? ExclamationTriangleFill : CheckCircleFill; + + return ( + + + + {} +
+ {message} + {errors.length > 0 && ( +
    + {errors.map((error, i) => { + return
  • {getErrorMessageFromField(error)}
  • ; + })} +
+ )} +
+
+
+
+ ); +}; + +export default Toast; From f55c35d056564fdd9b3b3e53f3678ca027d4fb80 Mon Sep 17 00:00:00 2001 From: Christian Maddox Date: Mon, 28 Oct 2024 08:44:11 -0400 Subject: [PATCH 9/9] chore: Replace Quantum w/ Oban for cron job. (#542) * Replace Quantum w/ Oban for cron job. * Fix schedule. * Add some useful plugins. * Temp: Test job for dev-green. * Revert "Temp: Test job for dev-green." This reverts commit 60c051b77df68082c94548b80812dc78067bd852. --- config/config.exs | 5 +++++ config/runtime.exs | 18 ++++++++++++------ config/test.exs | 2 ++ lib/screenplay/application.ex | 4 ++-- .../jobs/takeover_tool_testing_job.ex | 5 ++++- lib/screenplay/outfront/sftp.ex | 1 + lib/screenplay/scheduler.ex | 4 ---- mix.exs | 4 ++-- mix.lock | 6 ++---- .../20241025121807_add_oban_jobs_table.exs | 13 +++++++++++++ 10 files changed, 43 insertions(+), 19 deletions(-) delete mode 100644 lib/screenplay/scheduler.ex create mode 100644 priv/repo/migrations/20241025121807_add_oban_jobs_table.exs diff --git a/config/config.exs b/config/config.exs index d1abda82..990682a4 100644 --- a/config/config.exs +++ b/config/config.exs @@ -73,6 +73,11 @@ config :ueberauth, Ueberauth, config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase +config :screenplay, Oban, + engine: Oban.Engines.Basic, + queues: [default: 10], + repo: Screenplay.Repo + import_config "outfront_takeover_tool_screens.exs" # Import environment specific config. This must remain at the bottom diff --git a/config/runtime.exs b/config/runtime.exs index d314573c..2c0ec152 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -60,11 +60,17 @@ if sentry_dsn not in [nil, ""] do environment_name: env end -scheduler_jobs = - if env == "prod", - do: [{"0 7 * * *", {Screenplay.Jobs.TakeoverToolTestingJob, :run, []}}], - else: [] - -config :screenplay, Screenplay.Scheduler, jobs: scheduler_jobs +if env == "prod" do + config :screenplay, Oban, + plugins: [ + {Oban.Plugins.Cron, + crontab: [ + {"0 7 * * *", Screenplay.Jobs.TakeoverToolTestingJob} + ]}, + Oban.Plugins.Pruner, + Oban.Plugins.Lifeline, + Oban.Plugins.Reindexer + ] +end config :screenplay, Screenplay.Repo, pool_size: 10 diff --git a/config/test.exs b/config/test.exs index 10cb3eb2..5f02ca63 100644 --- a/config/test.exs +++ b/config/test.exs @@ -41,5 +41,7 @@ config :ueberauth_oidcc, config :screenplay, Screenplay.Repo, database: "screenplay_test", pool: Ecto.Adapters.SQL.Sandbox +config :screenplay, Oban, testing: :inline + # Print only warnings and errors during test config :logger, level: :warning diff --git a/lib/screenplay/application.ex b/lib/screenplay/application.ex index dcd6cb76..213434a8 100644 --- a/lib/screenplay/application.ex +++ b/lib/screenplay/application.ex @@ -11,6 +11,7 @@ defmodule Screenplay.Application do {Ecto.Migrator, repos: Application.fetch_env!(:screenplay, :ecto_repos)}, # Start the Ecto repository Screenplay.Repo, + {Oban, Application.fetch_env!(:screenplay, Oban)}, # Start the Telemetry supervisor ScreenplayWeb.Telemetry, # Start the PubSub system @@ -19,8 +20,7 @@ defmodule Screenplay.Application do ScreenplayWeb.Endpoint, Screenplay.OutfrontTakeoverTool.Alerts.State, Screenplay.OutfrontTakeoverTool.Alerts.Reminders, - Screenplay.ScreensConfig, - Screenplay.Scheduler + Screenplay.ScreensConfig ] ++ if Application.get_env(:screenplay, :start_cache_processes) do [Screenplay.Alerts.Cache, Screenplay.Places] diff --git a/lib/screenplay/jobs/takeover_tool_testing_job.ex b/lib/screenplay/jobs/takeover_tool_testing_job.ex index 9c522eb6..23e694ae 100644 --- a/lib/screenplay/jobs/takeover_tool_testing_job.ex +++ b/lib/screenplay/jobs/takeover_tool_testing_job.ex @@ -7,6 +7,8 @@ defmodule Screenplay.Jobs.TakeoverToolTestingJob do """ @dialyzer {:no_match, write_image: 3, delete_image: 2} + use Oban.Worker, unique: true + alias Screenplay.Outfront.SFTP require Logger @@ -16,7 +18,8 @@ defmodule Screenplay.Jobs.TakeoverToolTestingJob do @portrait_dir Application.compile_env!(:screenplay, :portrait_dir) @test_image :screenplay |> :code.priv_dir() |> Path.join("takeover_test.png") |> File.read!() - def run do + @impl Oban.Worker + def perform(_) do SFTP.run(fn conn -> test_creating_and_removing_images(conn) test_all_directories_exist(conn) diff --git a/lib/screenplay/outfront/sftp.ex b/lib/screenplay/outfront/sftp.ex index db5751b1..b723bea8 100644 --- a/lib/screenplay/outfront/sftp.ex +++ b/lib/screenplay/outfront/sftp.ex @@ -10,6 +10,7 @@ defmodule Screenplay.Outfront.SFTP do @portrait_dir Application.compile_env!(:screenplay, :portrait_dir) @retries 3 + @spec run((SFTPClient.Conn.t() -> :ok)) :: :ok def run(work_fn) do conn = start_connection() diff --git a/lib/screenplay/scheduler.ex b/lib/screenplay/scheduler.ex deleted file mode 100644 index 6832da51..00000000 --- a/lib/screenplay/scheduler.ex +++ /dev/null @@ -1,4 +0,0 @@ -defmodule Screenplay.Scheduler do - @moduledoc false - use Quantum, otp_app: :screenplay -end diff --git a/mix.exs b/mix.exs index 231b08dd..5f5268e7 100644 --- a/mix.exs +++ b/mix.exs @@ -66,12 +66,12 @@ defmodule Screenplay.MixProject do git: "https://github.com/mbta/screens-config-lib.git", ref: "8ec6e1684a129b089edc5e867a32dfc90028b2e0"}, {:mox, "~> 1.0", only: :test}, - {:quantum, "~> 3.0"}, {:tzdata, "~> 1.1"}, {:ex_machina, "~> 2.7", only: :test}, {:remote_ip, "~> 1.2"}, {:faker, "~> 0.18"}, - {:nebulex, "~> 2.6"} + {:nebulex, "~> 2.6"}, + {:oban, "~> 2.18"} ] end diff --git a/mix.lock b/mix.lock index b127a788..e00d5694 100644 --- a/mix.lock +++ b/mix.lock @@ -7,7 +7,6 @@ "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, "credo": {:hex, :credo, "1.7.8", "9722ba1681e973025908d542ec3d95db5f9c549251ba5b028e251ad8c24ab8c5", [: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", "cb9e87cc64f152f3ed1c6e325e7b894dea8f5ef2e41123bd864e3cd5ceb44968"}, - "crontab": {:hex, :crontab, "1.1.13", "3bad04f050b9f7f1c237809e42223999c150656a6b2afbbfef597d56df2144c5", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "d67441bec989640e3afb94e123f45a2bc42d76e02988c9613885dc3d01cf7085"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dialyxir": {:hex, :dialyxir, "1.4.4", "fb3ce8741edeaea59c9ae84d5cec75da00fa89fe401c72d6e047d11a61f65f70", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cd6111e8017ccd563e65621a4d9a4a1c5cd333df30cebc7face8029cacb4eff6"}, @@ -21,7 +20,6 @@ "expo": {:hex, :expo, "0.5.2", "beba786aab8e3c5431813d7a44b828e7b922bfa431d6bfbada0904535342efe2", [:mix], [], "hexpm", "8c9bfa06ca017c9cb4020fabe980bc7fdb1aaec059fd004c2ab3bff03b1c599c"}, "faker": {:hex, :faker, "0.18.0", "943e479319a22ea4e8e39e8e076b81c02827d9302f3d32726c5bf82f430e6e14", [:mix], [], "hexpm", "bfbdd83958d78e2788e99ec9317c4816e651ad05e24cfd1196ce5db5b3e81797"}, "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, - "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"}, "guardian": {:hex, :guardian, "2.3.2", "78003504b987f2b189d76ccf9496ceaa6a454bb2763627702233f31eb7212881", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "b189ff38cd46a22a8a824866a6867ca8722942347f13c33f7d23126af8821b52"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, @@ -37,6 +35,7 @@ "nebulex": {:hex, :nebulex, "2.6.4", "4b00706e0e676474783d988962abf74614480e13c0a32645acb89bb32b660e09", [: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", "25bdabf3fb86035c8151bba60bda20f80f96ae0261db7bd4090878ff63b03581"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_ownership": {:hex, :nimble_ownership, "0.3.2", "d4fa4056ade0ae33b5a9eb64554a1b3779689282e37513260125d2d6b32e4874", [:mix], [], "hexpm", "28b9a9f4094fda1aa8ca72f732ff3223eb54aa3eda4fed9022254de2c152b138"}, + "oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"}, "oidcc": {:hex, :oidcc, "3.2.4", "0bbe0f6059682dcb3bb70f682ede606779ea7b73337803a05c7d8eb3ae76acc0", [:mix, :rebar3], [{:jose, "~> 1.11", [hex: :jose, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_registry, "~> 0.3.1", [hex: :telemetry_registry, repo: "hexpm", optional: false]}], "hexpm", "984956348f6f833577b7a6cb72b325936cab3fd1c9cf28d7d54773d3ea48a20a"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"}, @@ -50,8 +49,7 @@ "plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"}, "plug_cowboy": {:hex, :plug_cowboy, "2.7.2", "fdadb973799ae691bf9ecad99125b16625b1c6039999da5fe544d99218e662e4", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "245d8a11ee2306094840c000e8816f0cbed69a23fc0ac2bcf8d7835ae019bb2f"}, "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, - "postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"}, - "quantum": {:hex, :quantum, "3.5.3", "ee38838a07761663468145f489ad93e16a79440bebd7c0f90dc1ec9850776d99", [:mix], [{:crontab, "~> 1.1", [hex: :crontab, repo: "hexpm", optional: false]}, {:gen_stage, "~> 0.14 or ~> 1.0", [hex: :gen_stage, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_registry, "~> 0.2", [hex: :telemetry_registry, repo: "hexpm", optional: false]}], "hexpm", "500fd3fa77dcd723ed9f766d4a175b684919ff7b6b8cfd9d7d0564d58eba8734"}, + "postgrex": {:hex, :postgrex, "0.19.2", "34d6884a332c7bf1e367fc8b9a849d23b43f7da5c6e263def92784d03f9da468", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "618988886ab7ae8561ebed9a3c7469034bf6a88b8995785a3378746a4b9835ec"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "remote_ip": {:hex, :remote_ip, "1.2.0", "fb078e12a44414f4cef5a75963c33008fe169b806572ccd17257c208a7bc760f", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "2ff91de19c48149ce19ed230a81d377186e4412552a597d6a5137373e5877cb7"}, "screens_config": {:git, "https://github.com/mbta/screens-config-lib.git", "8ec6e1684a129b089edc5e867a32dfc90028b2e0", [ref: "8ec6e1684a129b089edc5e867a32dfc90028b2e0"]}, diff --git a/priv/repo/migrations/20241025121807_add_oban_jobs_table.exs b/priv/repo/migrations/20241025121807_add_oban_jobs_table.exs new file mode 100644 index 00000000..7595a678 --- /dev/null +++ b/priv/repo/migrations/20241025121807_add_oban_jobs_table.exs @@ -0,0 +1,13 @@ +defmodule Screenplay.Repo.Migrations.AddObanJobsTable do + use Ecto.Migration + + def up do + Oban.Migration.up(version: 12) + end + + # We specify `version: 1` in `down`, ensuring that we'll roll all the way back down if + # necessary, regardless of which version we've migrated `up` to. + def down do + Oban.Migration.down(version: 1) + end +end