From 5162e1062bcab7d268e8a43660c4c433c322e744 Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Wed, 29 Nov 2023 11:18:32 -0700 Subject: [PATCH] feat: set up GraphQL API --- lib/mobile_app_backend_web/endpoint.ex | 2 +- lib/mobile_app_backend_web/resolvers/route.ex | 7 +++++++ lib/mobile_app_backend_web/resolvers/stop.ex | 8 ++++++++ lib/mobile_app_backend_web/router.ex | 10 ++++++++++ lib/mobile_app_backend_web/schema.ex | 15 +++++++++++++++ lib/mobile_app_backend_web/schema/route_types.ex | 16 ++++++++++++++++ lib/mobile_app_backend_web/schema/stop_types.ex | 16 ++++++++++++++++ mix.exs | 4 +++- mix.lock | 3 +++ 9 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 lib/mobile_app_backend_web/resolvers/route.ex create mode 100644 lib/mobile_app_backend_web/resolvers/stop.ex create mode 100644 lib/mobile_app_backend_web/schema.ex create mode 100644 lib/mobile_app_backend_web/schema/route_types.ex create mode 100644 lib/mobile_app_backend_web/schema/stop_types.ex diff --git a/lib/mobile_app_backend_web/endpoint.ex b/lib/mobile_app_backend_web/endpoint.ex index 929ef4cc..c1c9e5d8 100644 --- a/lib/mobile_app_backend_web/endpoint.ex +++ b/lib/mobile_app_backend_web/endpoint.ex @@ -41,7 +41,7 @@ defmodule MobileAppBackendWeb.Endpoint do plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] plug Plug.Parsers, - parsers: [:urlencoded, :multipart, :json], + parsers: [:urlencoded, :multipart, :json, Absinthe.Plug.Parser], pass: ["*/*"], json_decoder: Phoenix.json_library() diff --git a/lib/mobile_app_backend_web/resolvers/route.ex b/lib/mobile_app_backend_web/resolvers/route.ex new file mode 100644 index 00000000..d3e01db9 --- /dev/null +++ b/lib/mobile_app_backend_web/resolvers/route.ex @@ -0,0 +1,7 @@ +defmodule MobileAppBackendWeb.Resolvers.Route do + def by_stop(%Stops.Stop{id: stop_id}, _args, _resolution) do + {:ok, + Routes.Repo.by_stop_with_route_pattern(stop_id) + |> Enum.map(fn {route, route_patterns} -> Map.put(route, :route_patterns, route_patterns) end)} + end +end diff --git a/lib/mobile_app_backend_web/resolvers/stop.ex b/lib/mobile_app_backend_web/resolvers/stop.ex new file mode 100644 index 00000000..1c9d1c05 --- /dev/null +++ b/lib/mobile_app_backend_web/resolvers/stop.ex @@ -0,0 +1,8 @@ +defmodule MobileAppBackendWeb.Resolvers.Stop do + def find_stop(_parent, %{id: id}, _resolution) do + case Stops.Api.by_gtfs_id(id) do + {:ok, stop} when not is_nil(stop) -> {:ok, stop} + x -> {:error, x} + end + end +end diff --git a/lib/mobile_app_backend_web/router.ex b/lib/mobile_app_backend_web/router.ex index a2d4c726..fdf291e7 100644 --- a/lib/mobile_app_backend_web/router.ex +++ b/lib/mobile_app_backend_web/router.ex @@ -31,6 +31,16 @@ defmodule MobileAppBackendWeb.Router do get("/route/by-stop/:stop_id", RouteController, :by_stop) end + scope "/graphql" do + pipe_through :api + + forward "/graphiql", Absinthe.Plug.GraphiQL, + schema: MobileAppBackendWeb.Schema, + interface: :playground + + forward "/", Absinthe.Plug, schema: MobileAppBackendWeb.Schema + end + # Enable LiveDashboard and Swoosh mailbox preview in development if Application.compile_env(:mobile_app_backend, :dev_routes) do # If you want to use the LiveDashboard in production, you should put diff --git a/lib/mobile_app_backend_web/schema.ex b/lib/mobile_app_backend_web/schema.ex new file mode 100644 index 00000000..cc22cb4e --- /dev/null +++ b/lib/mobile_app_backend_web/schema.ex @@ -0,0 +1,15 @@ +defmodule MobileAppBackendWeb.Schema do + use Absinthe.Schema + import_types(MobileAppBackendWeb.Schema.RouteTypes) + import_types(MobileAppBackendWeb.Schema.StopTypes) + + alias MobileAppBackendWeb.Resolvers + + query do + @desc "Get a stop by ID" + field :stop, :stop do + arg(:id, non_null(:id)) + resolve(&Resolvers.Stop.find_stop/3) + end + end +end diff --git a/lib/mobile_app_backend_web/schema/route_types.ex b/lib/mobile_app_backend_web/schema/route_types.ex new file mode 100644 index 00000000..9c9f4fbf --- /dev/null +++ b/lib/mobile_app_backend_web/schema/route_types.ex @@ -0,0 +1,16 @@ +defmodule MobileAppBackendWeb.Schema.RouteTypes do + use Absinthe.Schema.Notation + + object :route do + field(:id, non_null(:id)) + field(:name, non_null(:string)) + field(:long_name, non_null(:string)) + + field(:route_patterns, list_of(:route_pattern)) + end + + object :route_pattern do + field(:id, non_null(:id)) + field(:name, non_null(:string)) + end +end diff --git a/lib/mobile_app_backend_web/schema/stop_types.ex b/lib/mobile_app_backend_web/schema/stop_types.ex new file mode 100644 index 00000000..187f02bd --- /dev/null +++ b/lib/mobile_app_backend_web/schema/stop_types.ex @@ -0,0 +1,16 @@ +defmodule MobileAppBackendWeb.Schema.StopTypes do + use Absinthe.Schema.Notation + + alias MobileAppBackendWeb.Resolvers + + object :stop do + field(:id, non_null(:id)) + field(:name, non_null(:string)) + field(:latitude, non_null(:float)) + field(:longitude, non_null(:float)) + + field :routes, list_of(:route) do + resolve(&Resolvers.Route.by_stop/3) + end + end +end diff --git a/mix.exs b/mix.exs index 59fa538a..23c0f363 100644 --- a/mix.exs +++ b/mix.exs @@ -65,7 +65,9 @@ defmodule MobileAppBackend.MixProject do {:bypass, "~> 2.1", only: :test}, {:httpoison, "~> 1.5"}, {:sentry, "~> 7.0"}, - {:con_cache, "~> 0.12.0"} + {:con_cache, "~> 0.12.0"}, + {:absinthe, "~> 1.7"}, + {:absinthe_plug, "~> 1.5"} ] end diff --git a/mix.lock b/mix.lock index 63f4523f..a2d9660a 100644 --- a/mix.lock +++ b/mix.lock @@ -1,4 +1,6 @@ %{ + "absinthe": {:hex, :absinthe, "1.7.6", "0b897365f98d068cfcb4533c0200a8e58825a4aeeae6ec33633ebed6de11773b", [:mix], [{:dataloader, "~> 1.0.0 or ~> 2.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:opentelemetry_process_propagator, "~> 0.2.1", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7626951ca5eec627da960615b51009f3a774765406ff02722b1d818f17e5778"}, + "absinthe_plug": {:hex, :absinthe_plug, "1.5.8", "38d230641ba9dca8f72f1fed2dfc8abd53b3907d1996363da32434ab6ee5d6ab", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bbb04176647b735828861e7b2705465e53e2cf54ccf5a73ddd1ebd855f996e5a"}, "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"}, "castore": {:hex, :castore, "1.0.4", "ff4d0fb2e6411c0479b1d965a814ea6d00e51eb2f58697446e9c41a97d940b28", [:mix], [], "hexpm", "9418c1b8144e11656f0be99943db4caf04612e3eaecefb5dae9a2a87565584f8"}, @@ -31,6 +33,7 @@ "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"}, "nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "phoenix": {:hex, :phoenix, "1.7.10", "02189140a61b2ce85bb633a9b6fd02dff705a5f1596869547aeb2b2b95edd729", [: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.6", [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", "cf784932e010fd736d656d7fead6a584a4498efefe5b8227e9f383bf15bb79d0"},