Skip to content

Commit 9c1543b

Browse files
authored
Add GraphQL endpoint (#39)
* Add Absinthe dependency * Add GraphQL schema, Film type and queries * Add Person type and queries * Add Planet type and queries * Add Species type and queries * Add Starship type and queries * Add Vehicle type and queries * Use dataloader to resolve references * Implement search queries * Move Dataloader to own module * Fix alias warnings * Fix typespecs * Add GraphQL resolver tests * Add tests for GraphQL queries * Add GraphQL sandbox, move APi endpoint to /api/graphql * Fix formatting * Add moduledocs * Fix string formatting * Fix remaining credo issues
1 parent a29d207 commit 9c1543b

39 files changed

Lines changed: 2184 additions & 2 deletions

.formatter.exs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
[
22
import_deps: [:ecto, :ecto_sql, :phoenix],
33
subdirectories: ["priv/*/migrations"],
4-
plugins: [Phoenix.LiveView.HTMLFormatter],
5-
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}", "priv/*/seeds.exs"]
4+
plugins: [Phoenix.LiveView.HTMLFormatter, Absinthe.Formatter],
5+
inputs: [
6+
"*.{ex,exs}",
7+
"{config,lib,test}/**/*.{ex,exs}",
8+
"priv/*/seeds.exs",
9+
"{lib,priv}/**/*.{gql,graphql}"
10+
]
611
]

lib/swapi/dataloader.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
defmodule SWAPI.Dataloader do
2+
@moduledoc """
3+
Dataloader for GraphQL
4+
"""
5+
6+
def data, do: Dataloader.Ecto.new(SWAPI.Repo, query: &query/2)
7+
8+
def query(queryable, _params), do: queryable
9+
end

lib/swapi_web/graphql/queries.ex

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
defmodule SWAPIWeb.GraphQL.Queries do
2+
@moduledoc """
3+
GraphQL queries
4+
"""
5+
6+
use Absinthe.Schema.Notation
7+
8+
import_types(SWAPIWeb.GraphQL.Queries.FilmQueries)
9+
import_types(SWAPIWeb.GraphQL.Queries.PersonQueries)
10+
import_types(SWAPIWeb.GraphQL.Queries.PlanetQueries)
11+
import_types(SWAPIWeb.GraphQL.Queries.SpeciesQueries)
12+
import_types(SWAPIWeb.GraphQL.Queries.StarshipQueries)
13+
import_types(SWAPIWeb.GraphQL.Queries.VehicleQueries)
14+
15+
object :queries do
16+
import_fields(:film_queries)
17+
import_fields(:person_queries)
18+
import_fields(:planet_queries)
19+
import_fields(:species_queries)
20+
import_fields(:starship_queries)
21+
import_fields(:vehicle_queries)
22+
end
23+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule SWAPIWeb.GraphQL.Queries.FilmQueries do
2+
@moduledoc """
3+
GraphQL queries for films
4+
"""
5+
6+
use Absinthe.Schema.Notation
7+
8+
alias SWAPIWeb.GraphQL.Resolvers.FilmResolver
9+
10+
object :film_queries do
11+
@desc "Get all films."
12+
field :all_films, list_of(:film) do
13+
resolve(&FilmResolver.all/2)
14+
end
15+
16+
@desc "Get a film by ID."
17+
field :film, :film do
18+
@desc "The ID of the film."
19+
arg(:id, non_null(:id))
20+
21+
resolve(&FilmResolver.one/2)
22+
end
23+
24+
@desc "Search films by title."
25+
field :search_films, list_of(:film) do
26+
@desc "A list of search terms. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched."
27+
arg(:search_terms, non_null(list_of(non_null(:string))))
28+
29+
resolve(&FilmResolver.search/2)
30+
end
31+
end
32+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule SWAPIWeb.GraphQL.Queries.PersonQueries do
2+
@moduledoc """
3+
GraphQL queries for people
4+
"""
5+
6+
use Absinthe.Schema.Notation
7+
8+
alias SWAPIWeb.GraphQL.Resolvers.PersonResolver
9+
10+
object :person_queries do
11+
@desc "Get all people."
12+
field :all_people, list_of(:person) do
13+
resolve(&PersonResolver.all/2)
14+
end
15+
16+
@desc "Get a person by ID."
17+
field :person, :person do
18+
@desc "The ID of the person."
19+
arg(:id, non_null(:id))
20+
21+
resolve(&PersonResolver.one/2)
22+
end
23+
24+
@desc "Search people by name."
25+
field :search_people, list_of(:person) do
26+
@desc "A list of search terms. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched."
27+
arg(:search_terms, non_null(list_of(non_null(:string))))
28+
29+
resolve(&PersonResolver.search/2)
30+
end
31+
end
32+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule SWAPIWeb.GraphQL.Queries.PlanetQueries do
2+
@moduledoc """
3+
GraphQL queries for planets
4+
"""
5+
6+
use Absinthe.Schema.Notation
7+
8+
alias SWAPIWeb.GraphQL.Resolvers.PlanetResolver
9+
10+
object :planet_queries do
11+
@desc "Get all planets."
12+
field :all_planets, list_of(:planet) do
13+
resolve(&PlanetResolver.all/2)
14+
end
15+
16+
@desc "Get a planet by ID."
17+
field :planet, :planet do
18+
@desc "The ID of the planet."
19+
arg(:id, non_null(:id))
20+
21+
resolve(&PlanetResolver.one/2)
22+
end
23+
24+
@desc "Search planets by name."
25+
field :search_planets, list_of(:planet) do
26+
@desc "A list of search terms. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched."
27+
arg(:search_terms, non_null(list_of(non_null(:string))))
28+
29+
resolve(&PlanetResolver.search/2)
30+
end
31+
end
32+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule SWAPIWeb.GraphQL.Queries.SpeciesQueries do
2+
@moduledoc """
3+
GraphQL queries for species
4+
"""
5+
6+
use Absinthe.Schema.Notation
7+
8+
alias SWAPIWeb.GraphQL.Resolvers.SpeciesResolver
9+
10+
object :species_queries do
11+
@desc "Get all species."
12+
field :all_species, list_of(:species) do
13+
resolve(&SpeciesResolver.all/2)
14+
end
15+
16+
@desc "Get a species by ID."
17+
field :species, :species do
18+
@desc "The ID of the species."
19+
arg(:id, non_null(:id))
20+
21+
resolve(&SpeciesResolver.one/2)
22+
end
23+
24+
@desc "Search species by name."
25+
field :search_species, list_of(:species) do
26+
@desc "A list of search terms. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched."
27+
arg(:search_terms, non_null(list_of(non_null(:string))))
28+
29+
resolve(&SpeciesResolver.search/2)
30+
end
31+
end
32+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule SWAPIWeb.GraphQL.Queries.StarshipQueries do
2+
@moduledoc """
3+
GraphQL queries for starships
4+
"""
5+
6+
use Absinthe.Schema.Notation
7+
8+
alias SWAPIWeb.GraphQL.Resolvers.StarshipResolver
9+
10+
object :starship_queries do
11+
@desc "Get all starships."
12+
field :all_starships, list_of(:starship) do
13+
resolve(&StarshipResolver.all/2)
14+
end
15+
16+
@desc "Get a starship by ID."
17+
field :starship, :starship do
18+
@desc "The ID of the starship."
19+
arg(:id, non_null(:id))
20+
21+
resolve(&StarshipResolver.one/2)
22+
end
23+
24+
@desc "Search starships by name or model."
25+
field :search_starships, list_of(:starship) do
26+
@desc "A list of search terms. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched."
27+
arg(:search_terms, non_null(list_of(non_null(:string))))
28+
29+
resolve(&StarshipResolver.search/2)
30+
end
31+
end
32+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule SWAPIWeb.GraphQL.Queries.VehicleQueries do
2+
@moduledoc """
3+
GraphQL queries for vehicles
4+
"""
5+
6+
use Absinthe.Schema.Notation
7+
8+
alias SWAPIWeb.GraphQL.Resolvers.VehicleResolver
9+
10+
object :vehicle_queries do
11+
@desc "Get all vehicles."
12+
field :all_vehicles, list_of(:vehicle) do
13+
resolve(&VehicleResolver.all/2)
14+
end
15+
16+
@desc "Get a vehicle by ID."
17+
field :vehicle, :vehicle do
18+
@desc "The ID of the vehicle."
19+
arg(:id, non_null(:id))
20+
21+
resolve(&VehicleResolver.one/2)
22+
end
23+
24+
@desc "Search vehicles by name or model."
25+
field :search_vehicles, list_of(:vehicle) do
26+
@desc "A list of search terms. If multiple search terms are used then objects will be returned in the list only if all the provided terms are matched."
27+
arg(:search_terms, non_null(list_of(non_null(:string))))
28+
29+
resolve(&VehicleResolver.search/2)
30+
end
31+
end
32+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
defmodule SWAPIWeb.GraphQL.Resolvers.FilmResolver do
2+
@moduledoc """
3+
Film resolver.
4+
"""
5+
6+
alias SWAPI.Films
7+
alias SWAPI.Schemas.Film
8+
9+
@spec all(map, map) :: {:ok, list(Film.t())} | {:error, any}
10+
def all(_args, _info) do
11+
{:ok, Films.list_films()}
12+
end
13+
14+
@spec one(map, Absinthe.Blueprint.t()) :: {:ok, Film.t()} | {:error, any}
15+
def one(%{id: id}, _info) do
16+
case Films.get_film(id) do
17+
{:ok, film} -> {:ok, film}
18+
{:error, :not_found} -> {:error, "Film not found"}
19+
end
20+
end
21+
22+
@spec search(map, Absinthe.Blueprint.t()) :: {:ok, list(Film.t())} | {:error, any}
23+
def search(%{search_terms: search_terms}, _info) do
24+
{:ok, Films.search_films(search_terms)}
25+
end
26+
end

0 commit comments

Comments
 (0)