Skip to content

Instantly share code, notes, and snippets.

@chris-ramon
Last active October 24, 2019 20:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chris-ramon/78027c8c0b283bfd1d20d6d989485e1d to your computer and use it in GitHub Desktop.
Save chris-ramon/78027c8c0b283bfd1d20d6d989485e1d to your computer and use it in GitHub Desktop.
Alternative solution to concurrent resolvers
/*
$ go run main.go
rootObject() | took: 1.000260951s
graphql.Do() | took: 367.401µs
graphql.Do() | result: {"data":{"me":{"github":{"issues":[{"id":"100"},{"id":"101"}],"pullRequests":[{"id":"200"},{"id":"201"}]}}}}
*/
package main
import (
"encoding/json"
"fmt"
"log"
"sync"
"time"
"github.com/graphql-go/graphql"
sourceAST "github.com/graphql-go/graphql/language/ast"
"github.com/graphql-go/graphql/language/parser"
"github.com/graphql-go/graphql/language/visitor"
)
type Issue struct {
Id string `graphql:"id"`
}
type PullRequest struct {
Id string `graphql:"id"`
}
type Me struct {
GitHub GitHub
}
type GitHub struct {
Issues []Issue `graphql:"issues"`
PullRequests []PullRequest `graphql:"pullRequests"`
}
var IssueType = graphql.NewObject(graphql.ObjectConfig{
Name: "Issue",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.String,
},
},
})
var PullRequestType = graphql.NewObject(graphql.ObjectConfig{
Name: "PullRequest",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.String,
},
},
})
var GitHubType = graphql.NewObject(graphql.ObjectConfig{
Name: "GitHub",
Fields: graphql.Fields{
"issues": &graphql.Field{
Type: graphql.NewList(IssueType),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return p.Source.(*GitHub).Issues, nil
},
},
"pullRequests": &graphql.Field{
Type: graphql.NewList(PullRequestType),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return p.Source.(*GitHub).PullRequests, nil
},
},
},
})
var MeType = graphql.NewObject(graphql.ObjectConfig{
Name: "Me",
Fields: graphql.Fields{
"github": &graphql.Field{
Type: GitHubType,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
rootValue := p.Info.RootValue.(map[string]interface{})
me, ok := rootValue["me"].(Me)
if ok {
return &me.GitHub, nil
}
return &GitHub{}, nil
},
},
},
})
var QueryType = graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"me": &graphql.Field{
Type: MeType,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return &Me{}, nil
},
},
},
})
func main() {
schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: QueryType,
})
if err != nil {
log.Fatal(err)
}
query := `
query {
me {
github {
issues {
id
}
pullRequests {
id
}
}
}
}
`
var rootObj map[string]interface{}
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
t := time.Now()
ro, err := rootObject(query)
if err != nil {
log.Println(err)
}
rootObj = ro
fmt.Printf("rootObject() | took: %v\n", time.Since(t))
}()
wg.Wait()
t := time.Now()
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
RootObject: rootObj,
})
fmt.Printf("graphql.Do() | took: %v\n", time.Since(t))
if len(result.Errors) > 0 {
log.Fatal(result.Errors)
}
b, err := json.Marshal(result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("graphql.Do() | result: %s\n", b)
}
func rootObject(query string) (map[string]interface{}, error) {
obj := make(map[string]interface{}, 1)
fields, err := queryFields(query)
if err != nil {
return obj, err
}
var issuesRequested bool
var pullRequestsRequested bool
for _, f := range fields {
if f == "issues" {
issuesRequested = true
}
if f == "pullRequests" {
pullRequestsRequested = true
}
}
var wg sync.WaitGroup
var issues []Issue
if issuesRequested {
wg.Add(1)
go func() {
defer wg.Done()
i, err := lookupIssues()
if err != nil {
log.Println(err)
}
issues = i
}()
}
var pullRequests []PullRequest
if pullRequestsRequested {
wg.Add(1)
go func() {
defer wg.Done()
pr, err := lookupPullRequests()
if err != nil {
log.Println(err)
}
pullRequests = pr
}()
}
wg.Wait()
obj["me"] = Me{
GitHub: GitHub{
Issues: issues,
PullRequests: pullRequests,
},
}
return obj, nil
}
func queryFields(query string) ([]string, error) {
var fields []string
ast, err := parser.Parse(parser.ParseParams{Source: query})
if err != nil {
return fields, err
}
v := &visitor.VisitorOptions{
Enter: func(p visitor.VisitFuncParams) (string, interface{}) {
if node, ok := p.Node.(*sourceAST.Field); ok {
if node.SelectionSet == nil {
return visitor.ActionNoChange, nil
}
fields = append(fields, node.Name.Value)
}
return visitor.ActionNoChange, nil
},
}
visitor.Visit(ast, v, nil)
return fields, nil
}
func lookupIssues() ([]Issue, error) {
time.Sleep(1 * time.Second)
issues := []Issue{
Issue{Id: "100"},
Issue{Id: "101"},
}
return issues, nil
}
func lookupPullRequests() ([]PullRequest, error) {
time.Sleep(1 * time.Second)
pullRequests := []PullRequest{
PullRequest{Id: "200"},
PullRequest{Id: "201"},
}
return pullRequests, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment