Last active
October 24, 2019 20:37
-
-
Save chris-ramon/78027c8c0b283bfd1d20d6d989485e1d to your computer and use it in GitHub Desktop.
Alternative solution to concurrent resolvers
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
$ 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