diff --git a/example/caller.go b/example/caller.go index 2489345..a1e1d77 100644 --- a/example/caller.go +++ b/example/caller.go @@ -4,10 +4,13 @@ import ( "context" "fmt" "os" + + "github.com/Khan/genql/graphql" ) func Main() { - resp, err := getViewer(context.Background()) + client := graphql.NewClient("https://api.github.com/graphql", nil) + resp, err := getViewer(context.Background(), client) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/example/generated.go b/example/generated.go index 58c15ed..f16c12c 100755 --- a/example/generated.go +++ b/example/generated.go @@ -2,13 +2,8 @@ package example import ( "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strings" - "github.com/vektah/gqlparser/gqlerror" + "github.com/Khan/genql/graphql" ) type getViewerResponse = struct { @@ -18,49 +13,14 @@ type getViewerResponse = struct { } // TODO -func getViewer(ctx context.Context) (*getViewerResponse, error) { - req, err := http.NewRequest( - http.MethodPost, - `https://api.github.com/graphql`, - strings.NewReader(` +func getViewer(ctx context.Context, client *graphql.Client) (*getViewerResponse, error) { + var retval getViewerResponse + err := client.MakeRequest(ctx, ` query getViewer { Viewer: viewer { Name: name } } -`)) - if err != nil { - return nil, err - } - - req = req.WithContext(ctx) - resp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("returned error %v: %v", resp.Status, string(body)) - } - - var retval struct { - Data getViewerResponse `json:"data"` - Errors gqlerror.List `json:"errors"` - } - err = json.Unmarshal(body, &retval) - if err != nil { - return nil, err - } - - if len(retval.Errors) > 0 { - return nil, retval.Errors - } - - return &retval.Data, nil +`, &retval) + return &retval, err } diff --git a/generate/generate.go b/generate/generate.go index 49c70cd..45e8603 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -40,8 +40,6 @@ type OperationParams struct { OperationName string // The documentation for the operation, from GraphQL. OperationDoc string - // The endpoint to which to send queries. - Endpoint string // The body of the operation to send. Operation string } @@ -89,16 +87,15 @@ func Generate(specFilename, schemaFilename, generatedFilename string) error { if generatedFilename == "-" { out = os.Stdout } else { - out, err = os.OpenFile(generatedFilename, os.O_RDWR|os.O_CREATE, 0755) + out, err = os.OpenFile(generatedFilename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) if err != nil { return fmt.Errorf("could not open generated file %v: %v", generatedFilename, err) } } - // TODO: configure these + // TODO: configure this packageName := "example" - endpoint := "https://api.github.com/graphql" // TODO: this should probably get factored out operations := make([]OperationParams, len(document.Operations)) @@ -125,7 +122,6 @@ func Generate(specFilename, schemaFilename, generatedFilename string) error { ResponseName: operation.Name + "Response", ResponseType: typeFor(operation, schema), - Endpoint: endpoint, // The newline just makes it format a little nicer Operation: "\n" + builder.String(), } diff --git a/generate/operation.go.tmpl b/generate/operation.go.tmpl index 89aff2a..451e687 100644 --- a/generate/operation.go.tmpl +++ b/generate/operation.go.tmpl @@ -2,57 +2,17 @@ package {{.PackageName}} import ( "context" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "strings" - "github.com/vektah/gqlparser/gqlerror" + "github.com/Khan/genql/graphql" ) {{range .Operations}} type {{.ResponseName}} = {{.ResponseType}} // {{.OperationDoc}} -func {{.OperationName}}(ctx context.Context) (*{{.ResponseName}}, error) { - req, err := http.NewRequest( - http.MethodPost, - `{{.Endpoint}}`, - strings.NewReader(`{{.Operation}}`)) - if err != nil { - return nil, err - } - - req = req.WithContext(ctx) - resp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("returned error %v: %v", resp.Status, string(body)) - } - - var retval struct { - Data {{.ResponseName}} `json:"data"` - Errors gqlerror.List `json:"errors"` - } - err = json.Unmarshal(body, &retval) - if err != nil { - return nil, err - } - - if len(retval.Errors) > 0 { - return nil, retval.Errors - } - - return &retval.Data, nil +func {{.OperationName}}(ctx context.Context, client *graphql.Client) (*{{.ResponseName}}, error) { + var retval {{.ResponseName}} + err := client.MakeRequest(ctx, `{{.Operation}}`, &retval) + return &retval, err } {{end}} diff --git a/go.mod b/go.mod index da65c96..35533c5 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,9 @@ module github.com/Khan/genql go 1.13 -require github.com/vektah/gqlparser v1.2.0 +require ( + github.com/Khan/graphql v0.0.0-20191109005718-3b51154b2bc5 + github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect + github.com/vektah/gqlparser v1.2.0 + golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect +) diff --git a/go.sum b/go.sum index 51cf77f..22255cf 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,22 @@ +github.com/Khan/graphql v0.0.0-20191109005718-3b51154b2bc5 h1:C7qbo6snTa0/lSpVKYhXiw3dJ/9FYiwZSsU+4Bov9qg= +github.com/Khan/graphql v0.0.0-20191109005718-3b51154b2bc5/go.mod h1:DLkRTcWV7KR4zw1iTm1lpShI3XP3nFmIMs3fHxYAsF0= github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f h1:tygelZueB1EtXkPI6mQ4o9DQ0+FKW41hTbunoXZCTqk= +github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/vektah/gqlparser v1.2.0 h1:ntkSCX7F5ZJKl+HIVnmLaO269MruasVpNiMOjX9kgo0= github.com/vektah/gqlparser v1.2.0/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/graphql/client.go b/graphql/client.go new file mode 100644 index 0000000..2f043d2 --- /dev/null +++ b/graphql/client.go @@ -0,0 +1,67 @@ +package graphql + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + + "github.com/vektah/gqlparser/gqlerror" +) + +type Client struct { + endpoint string + method string + httpClient *http.Client +} + +func NewClient(endpoint string, httpClient *http.Client) *Client { + if httpClient == nil { + httpClient = http.DefaultClient + } + return &Client{endpoint, http.MethodPost, httpClient} +} + +func (client *Client) MakeRequest(ctx context.Context, query string, retval interface{}) error { + req, err := http.NewRequest( + client.method, + client.endpoint, + strings.NewReader(query)) + if err != nil { + return err + } + + req = req.WithContext(ctx) + resp, err := client.httpClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("returned error %v: %v", resp.Status, string(body)) + } + + var dataAndErrors struct { + Data json.RawMessage `json:"data"` + Errors gqlerror.List `json:"errors"` + } + + err = json.Unmarshal(body, &dataAndErrors) + if err != nil { + return err + } + + if len(dataAndErrors.Errors) > 0 { + return dataAndErrors.Errors + } + + return json.Unmarshal(dataAndErrors.Data, &retval) +}