fill out the easy parts of the codegen

This commit is contained in:
Ben Kraft
2019-12-23 21:07:07 -05:00
parent efa9ccd122
commit 7a20e2ae74
5 changed files with 100 additions and 25 deletions
+1 -8
View File
@@ -1,16 +1,9 @@
package main package main
import ( import (
"fmt"
"os"
"github.com/Khan/genql/generate" "github.com/Khan/genql/generate"
) )
func main() { func main() {
err := generate.Generate() generate.Main()
if err != nil {
fmt.Println(fmt.Errorf("genql failed: %v", err))
os.Exit(1)
}
} }
+3 -4
View File
@@ -20,11 +20,10 @@ func GetViewer(ctx context.Context) (*GetViewerResponse, error) {
http.MethodPost, http.MethodPost,
`https://api.github.com/graphql`, `https://api.github.com/graphql`,
strings.NewReader(` strings.NewReader(`
"GetViewer gets the current user's name."
query GetViewer { query GetViewer {
Viewer: viewer { Viewer: viewer {
Name: name Name: name
} }
} }
`)) `))
if err != nil { if err != nil {
+1 -1
View File
@@ -1,4 +1,4 @@
"GetViewer gets the current user's name." # GetViewer gets the current user's name.
query GetViewer { query GetViewer {
Viewer: viewer { Viewer: viewer {
Name: name Name: name
+92 -11
View File
@@ -2,41 +2,122 @@ package generate
import ( import (
"fmt" "fmt"
"io"
"io/ioutil"
"os" "os"
"strings"
"text/template" "text/template"
"github.com/vektah/gqlparser" "github.com/vektah/gqlparser/ast"
"github.com/vektah/gqlparser/formatter"
"github.com/vektah/gqlparser/parser"
) )
var _ = gqlparser.LoadSchema
// TODO: package template into the binary using one of those asset thingies // TODO: package template into the binary using one of those asset thingies
const tmplFilename = "generate/operation.go.tmpl" const tmplFilename = "generate/operation.go.tmpl"
var tmpl = template.Must(template.ParseFiles(tmplFilename))
type TemplateParams struct { type TemplateParams struct {
// The name of the package into which to generate the operation-helpers. // The name of the package into which to generate the operation-helpers.
PackageName string PackageName string
// The list of operations for which to generate code.
Operations []OperationParams
}
type OperationParams struct {
// The type-name for the operation's response type. // The type-name for the operation's response type.
ResponseName string ResponseName string
// The body of the operation's response type (e.g. struct { ... }). // The body of the operation's response type (e.g. struct { ... }).
ResponseType string ResponseType string
// The documentation for the operation, from GraphQL. // The type of the operation (query, mutation, or subscription).
OperationDoc string OperationType ast.Operation
// The name of the operation, from GraphQL. // The name of the operation, from GraphQL.
OperationName string OperationName string
// The documentation for the operation, from GraphQL.
OperationDoc string
// The endpoint to which to send queries. // The endpoint to which to send queries.
Endpoint string Endpoint string
// The body of the operation to send. // The body of the operation to send.
Operation string Operation string
} }
var tmpl = template.Must(template.ParseFiles(tmplFilename)) func Generate(specFilename, generatedFilename string) error {
text, err := ioutil.ReadFile(specFilename)
func Generate() error {
var data TemplateParams
err := tmpl.Execute(os.Stdout, data)
if err != nil { if err != nil {
return fmt.Errorf("template did not render: %v", err) return fmt.Errorf("could not open query-spec file %v: %v",
specFilename, err)
}
document, graphqlError := parser.ParseQuery(
&ast.Source{Name: specFilename, Input: string(text)})
if graphqlError != nil { // ParseQuery returns type *graphql.Error, yuck
return fmt.Errorf("could not parse query-spec file %v: %v",
specFilename, graphqlError)
}
var out io.Writer
if generatedFilename == "-" {
out = os.Stdout
} else {
out, err = os.OpenFile(generatedFilename, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return fmt.Errorf("could not open generated file %v: %v",
generatedFilename, err)
}
}
// TODO: configure these
packageName := "example"
endpoint := "https://api.github.com/graphql"
operations := make([]OperationParams, len(document.Operations))
for i, operation := range document.Operations {
var builder strings.Builder
f := formatter.NewFormatter(&builder)
f.FormatQueryDocument(&ast.QueryDocument{
Operations: ast.OperationList{operation},
// TODO: handle fragments
})
operations[i] = OperationParams{
OperationType: operation.Operation,
OperationName: operation.Name,
OperationDoc: "TODO",
// TODO: configure this
ResponseName: operation.Name + "Response",
ResponseType: "struct{} // TODO",
Endpoint: endpoint,
// The newline just makes it format a little nicer
Operation: "\n" + builder.String(),
}
}
data := TemplateParams{
PackageName: packageName,
Operations: operations,
}
err = tmpl.Execute(out, data)
if err != nil {
return fmt.Errorf("could not render template: %v", err)
} }
return nil return nil
} }
func Main() {
var err error
defer func() {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}()
if len(os.Args) != 3 {
err = fmt.Errorf("usage: %s queries.graphql generated.go", os.Args[0])
return
}
err = Generate(os.Args[1], os.Args[2])
}
+3 -1
View File
@@ -8,6 +8,7 @@ import (
"strings" "strings"
) )
{{range .Operations}}
type {{.ResponseName}} = {{.ResponseType}} type {{.ResponseName}} = {{.ResponseType}}
// {{.OperationDoc}} // {{.OperationDoc}}
@@ -20,7 +21,7 @@ func {{.OperationName}}(ctx context.Context) (*{{.ResponseName}}, error) {
return nil, err return nil, err
} }
req = req.WithContext(ctx) req = req.WithContext(ctx)
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -40,3 +41,4 @@ func {{.OperationName}}(ctx context.Context) (*{{.ResponseName}}, error) {
return &retval, nil return &retval, nil
} }
{{end}}