_examples/starwars/resolvers.go (328 lines of code) (raw):
//go:generate rm -rf generated
//go:generate go run ../../testdata/gqlgen.go
package starwars
import (
"context"
"encoding/base64"
"errors"
"strconv"
"strings"
"time"
"github.com/99designs/gqlgen/_examples/starwars/generated"
"github.com/99designs/gqlgen/_examples/starwars/models"
)
type Resolver struct {
humans map[string]models.Human
droid map[string]models.Droid
starships map[string]models.Starship
reviews map[models.Episode][]*models.Review
}
func (r *Resolver) Droid() generated.DroidResolver {
return &droidResolver{r}
}
func (r *Resolver) FriendsConnection() generated.FriendsConnectionResolver {
return &friendsConnectionResolver{r}
}
func (r *Resolver) Human() generated.HumanResolver {
return &humanResolver{r}
}
func (r *Resolver) Mutation() generated.MutationResolver {
return &mutationResolver{r}
}
func (r *Resolver) Query() generated.QueryResolver {
return &queryResolver{r}
}
func (r *Resolver) Starship() generated.StarshipResolver {
return &starshipResolver{r}
}
func (r *Resolver) resolveCharacters(ctx context.Context, ids []string) ([]models.Character, error) {
result := make([]models.Character, len(ids))
for i, id := range ids {
char, err := r.Query().Character(ctx, id)
if err != nil {
return nil, err
}
result[i] = char
}
return result, nil
}
type droidResolver struct{ *Resolver }
func (r *droidResolver) Friends(ctx context.Context, obj *models.Droid) ([]models.Character, error) {
return r.resolveCharacters(ctx, obj.FriendIds)
}
func (r *droidResolver) FriendsConnection(ctx context.Context, obj *models.Droid, first *int, after *string) (*models.FriendsConnection, error) {
return r.resolveFriendConnection(ctx, obj.FriendIds, first, after)
}
type friendsConnectionResolver struct{ *Resolver }
func (r *Resolver) resolveFriendConnection(_ context.Context, ids []string, first *int, after *string) (*models.FriendsConnection, error) {
from := 0
if after != nil {
b, err := base64.StdEncoding.DecodeString(*after)
if err != nil {
return nil, err
}
i, err := strconv.Atoi(strings.TrimPrefix(string(b), "cursor"))
if err != nil {
return nil, err
}
from = i
}
to := len(ids)
if first != nil {
to = from + *first
if to > len(ids) {
to = len(ids)
}
}
return &models.FriendsConnection{
Ids: ids,
From: from,
To: to,
}, nil
}
func (r *friendsConnectionResolver) Edges(ctx context.Context, obj *models.FriendsConnection) ([]*models.FriendsEdge, error) {
friends, err := r.resolveCharacters(ctx, obj.Ids)
if err != nil {
return nil, err
}
edges := make([]*models.FriendsEdge, obj.To-obj.From)
for i := range edges {
edges[i] = &models.FriendsEdge{
Cursor: models.EncodeCursor(obj.From + i),
Node: friends[obj.From+i],
}
}
return edges, nil
}
func (r *friendsConnectionResolver) Friends(ctx context.Context, obj *models.FriendsConnection) ([]models.Character, error) {
return r.resolveCharacters(ctx, obj.Ids)
}
type humanResolver struct{ *Resolver }
func (r *humanResolver) Friends(ctx context.Context, obj *models.Human) ([]models.Character, error) {
return r.resolveCharacters(ctx, obj.FriendIds)
}
func (r *humanResolver) FriendsConnection(ctx context.Context, obj *models.Human, first *int, after *string) (*models.FriendsConnection, error) {
return r.resolveFriendConnection(ctx, obj.FriendIds, first, after)
}
func (r *humanResolver) Starships(ctx context.Context, obj *models.Human) ([]*models.Starship, error) {
var result []*models.Starship
for _, id := range obj.StarshipIds {
char, err := r.Query().Starship(ctx, id)
if err != nil {
return nil, err
}
if char != nil {
result = append(result, char)
}
}
return result, nil
}
type mutationResolver struct{ *Resolver }
func (r *mutationResolver) CreateReview(ctx context.Context, episode models.Episode, review models.Review) (*models.Review, error) {
review.Time = time.Now()
time.Sleep(1 * time.Second)
r.reviews[episode] = append(r.reviews[episode], &review)
return &review, nil
}
type queryResolver struct{ *Resolver }
func (r *queryResolver) Hero(ctx context.Context, episode *models.Episode) (models.Character, error) {
if *episode == models.EpisodeEmpire {
return r.humans["1000"], nil
}
return r.droid["2001"], nil
}
func (r *queryResolver) Reviews(ctx context.Context, episode models.Episode, since *time.Time) ([]*models.Review, error) {
if since == nil {
return r.reviews[episode], nil
}
var filtered []*models.Review
for _, rev := range r.reviews[episode] {
if rev.Time.After(*since) {
filtered = append(filtered, rev)
}
}
return filtered, nil
}
func (r *queryResolver) Search(ctx context.Context, text string) ([]models.SearchResult, error) {
var l []models.SearchResult
for _, h := range r.humans {
if strings.Contains(h.Name, text) {
l = append(l, h)
}
}
for _, d := range r.droid {
if strings.Contains(d.Name, text) {
l = append(l, d)
}
}
for _, s := range r.starships {
if strings.Contains(s.Name, text) {
l = append(l, s)
}
}
return l, nil
}
func (r *queryResolver) Character(ctx context.Context, id string) (models.Character, error) {
if h, ok := r.humans[id]; ok {
return &h, nil
}
if d, ok := r.droid[id]; ok {
return &d, nil
}
return nil, nil
}
func (r *queryResolver) Droid(ctx context.Context, id string) (*models.Droid, error) {
if d, ok := r.droid[id]; ok {
return &d, nil
}
return nil, nil
}
func (r *queryResolver) Human(ctx context.Context, id string) (*models.Human, error) {
if h, ok := r.humans[id]; ok {
return &h, nil
}
return nil, nil
}
func (r *queryResolver) Starship(ctx context.Context, id string) (*models.Starship, error) {
if s, ok := r.starships[id]; ok {
return &s, nil
}
return nil, nil
}
type starshipResolver struct{ *Resolver }
func (r *starshipResolver) Length(ctx context.Context, obj *models.Starship, unit *models.LengthUnit) (float64, error) {
switch *unit {
case models.LengthUnitMeter, "":
return obj.Length, nil
case models.LengthUnitFoot:
return obj.Length * 3.28084, nil
default:
return 0, errors.New("invalid unit")
}
}
func NewResolver() generated.Config {
r := Resolver{}
r.humans = map[string]models.Human{
"1000": {
CharacterFields: models.CharacterFields{
ID: "1000",
Name: "Luke Skywalker",
FriendIds: []string{"1002", "1003", "2000", "2001"},
AppearsIn: []models.Episode{models.EpisodeNewhope, models.EpisodeEmpire, models.EpisodeJedi},
},
HeightMeters: 1.72,
Mass: 77,
StarshipIds: []string{"3001", "3003"},
},
"1001": {
CharacterFields: models.CharacterFields{
ID: "1001",
Name: "Darth Vader",
FriendIds: []string{"1004"},
AppearsIn: []models.Episode{models.EpisodeNewhope, models.EpisodeEmpire, models.EpisodeJedi},
},
HeightMeters: 2.02,
Mass: 136,
StarshipIds: []string{"3002"},
},
"1002": {
CharacterFields: models.CharacterFields{
ID: "1002",
Name: "Han Solo",
FriendIds: []string{"1000", "1003", "2001"},
AppearsIn: []models.Episode{models.EpisodeNewhope, models.EpisodeEmpire, models.EpisodeJedi},
},
HeightMeters: 1.8,
Mass: 80,
StarshipIds: []string{"3000", "3003"},
},
"1003": {
CharacterFields: models.CharacterFields{
ID: "1003",
Name: "Leia Organa",
FriendIds: []string{"1000", "1002", "2000", "2001"},
AppearsIn: []models.Episode{models.EpisodeNewhope, models.EpisodeEmpire, models.EpisodeJedi},
},
HeightMeters: 1.5,
Mass: 49,
},
"1004": {
CharacterFields: models.CharacterFields{
ID: "1004",
Name: "Wilhuff Tarkin",
FriendIds: []string{"1001"},
AppearsIn: []models.Episode{models.EpisodeNewhope},
},
HeightMeters: 1.8,
Mass: 0,
},
}
r.droid = map[string]models.Droid{
"2000": {
CharacterFields: models.CharacterFields{
ID: "2000",
Name: "C-3PO",
FriendIds: []string{"1000", "1002", "1003", "2001"},
AppearsIn: []models.Episode{models.EpisodeNewhope, models.EpisodeEmpire, models.EpisodeJedi},
},
PrimaryFunction: "Protocol",
},
"2001": {
CharacterFields: models.CharacterFields{
ID: "2001",
Name: "R2-D2",
FriendIds: []string{"1000", "1002", "1003"},
AppearsIn: []models.Episode{models.EpisodeNewhope, models.EpisodeEmpire, models.EpisodeJedi},
},
PrimaryFunction: "Astromech",
},
}
r.starships = map[string]models.Starship{
"3000": {
ID: "3000",
Name: "Millennium Falcon",
History: [][]int{
{1, 2},
{4, 5},
{1, 2},
{3, 2},
},
Length: 34.37,
},
"3001": {
ID: "3001",
Name: "X-Wing",
History: [][]int{
{6, 4},
{3, 2},
{2, 3},
{5, 1},
},
Length: 12.5,
},
"3002": {
ID: "3002",
Name: "TIE Advanced x1",
History: [][]int{
{3, 2},
{7, 2},
{6, 4},
{3, 2},
},
Length: 9.2,
},
"3003": {
ID: "3003",
Name: "Imperial shuttle",
History: [][]int{
{1, 7},
{3, 5},
{5, 3},
{7, 1},
},
Length: 20,
},
}
r.reviews = map[models.Episode][]*models.Review{}
return generated.Config{
Resolvers: &r,
}
}