genql: quick hack at variables

This commit is contained in:
Ben Kraft
2020-01-02 17:52:55 -08:00
parent 77670363d3
commit 9746c818fd
7 changed files with 111 additions and 30 deletions
+14 -2
View File
@@ -34,6 +34,12 @@ func Main() {
return
}
if len(os.Args) != 2 {
err = fmt.Errorf("usage: %v <username>", os.Args[0])
return
}
username := os.Args[1]
httpClient := http.Client{
Transport: &authedTransport{
key: key,
@@ -41,10 +47,16 @@ func Main() {
},
}
graphqlClient := graphql.NewClient("https://api.github.com/graphql", &httpClient)
resp, err := getViewer(context.Background(), graphqlClient)
viewerResp, err := getViewer(context.Background(), graphqlClient)
if err != nil {
return
}
fmt.Println("you are", *viewerResp.Viewer.Name)
fmt.Println("you are:", *resp.Viewer.Name)
userResp, err := getUser(context.Background(), graphqlClient, username)
if err != nil {
return
}
fmt.Println(username, "is", *userResp.User.Name)
}
+27 -1
View File
@@ -14,6 +14,7 @@ type getViewerResponse = struct {
// TODO
func getViewer(ctx context.Context, client *graphql.Client) (*getViewerResponse, error) {
var retval getViewerResponse
err := client.MakeRequest(ctx, `
query getViewer {
@@ -21,6 +22,31 @@ query getViewer {
Name: name
}
}
`, &retval)
`, &retval, nil)
return &retval, err
}
type getUserResponse = struct {
User *struct {
Name *string
}
}
// TODO
func getUser(ctx context.Context, client *graphql.Client, login string) (*getUserResponse, error) {
variables := map[string]interface{}{
"login": login,
}
var retval getUserResponse
err := client.MakeRequest(ctx, `
query getUser ($login: String!) {
User: user(login: $login) {
Name: name
}
}
`, &retval, variables)
return &retval, err
}
+7
View File
@@ -4,3 +4,10 @@ query getViewer {
Name: name
}
}
# getUser gets the given user's name.
query getUser($login: String!) {
User: user(login: $login) {
Name: name
}
}
+38 -19
View File
@@ -26,22 +26,31 @@ type TemplateParams struct {
// The name of the package into which to generate the operation-helpers.
PackageName string
// The list of operations for which to generate code.
Operations []OperationParams
Operations []Operation
}
type OperationParams struct {
type Operation struct {
// The type of the operation (query, mutation, or subscription).
Type ast.Operation
// The name of the operation, from GraphQL.
Name string
// The documentation for the operation, from GraphQL.
Doc string
// The body of the operation to send.
Body string
// The arguments to the operation.
Args []Argument
// The type-name for the operation's response type.
ResponseName string
// The body of the operation's response type (e.g. struct { ... }).
ResponseType string
// The type of the operation (query, mutation, or subscription).
OperationType ast.Operation
// The name of the operation, from GraphQL.
OperationName string
// The documentation for the operation, from GraphQL.
OperationDoc string
// The body of the operation to send.
Operation string
}
type Argument struct {
GoName string
GoType string
GraphQLName string
}
func Generate(specFilename, schemaFilename, generatedFilename string) error {
@@ -98,7 +107,7 @@ func Generate(specFilename, schemaFilename, generatedFilename string) error {
packageName := "example"
// TODO: this should probably get factored out
operations := make([]OperationParams, len(document.Operations))
operations := make([]Operation, len(document.Operations))
for i, operation := range document.Operations {
// TODO: we may have to actually get the precise query text, in case we
// want to be hashing it or something like that. Although maybe
@@ -110,20 +119,30 @@ func Generate(specFilename, schemaFilename, generatedFilename string) error {
Operations: ast.OperationList{operation},
// TODO: handle fragments
})
operations[i] = OperationParams{
OperationType: operation.Operation,
OperationName: operation.Name,
args := make([]Argument, len(operation.VariableDefinitions))
for i, arg := range operation.VariableDefinitions {
args[i] = Argument{
GraphQLName: arg.Variable,
GoName: arg.Variable, // TODO: normalize this to go-style
GoType: typeForInputType(arg.Type, schema),
// TODO: figure out what to do about defaults
}
}
operations[i] = Operation{
Type: operation.Operation,
Name: operation.Name,
// TODO: this is actually awkward, because GraphQL doesn't allow
// for docstrings on queries (only schemas). So we have to extract
// the comment, or omit doc-comments for now.
OperationDoc: "TODO",
Doc: "TODO",
// The newline just makes it format a little nicer
Body: "\n" + builder.String(),
Args: args,
// TODO: configure ResponseName format
ResponseName: operation.Name + "Response",
ResponseType: typeFor(operation, schema),
// The newline just makes it format a little nicer
Operation: "\n" + builder.String(),
ResponseType: typeForOperation(operation, schema),
}
}
+10 -3
View File
@@ -9,10 +9,17 @@ import (
{{range .Operations}}
type {{.ResponseName}} = {{.ResponseType}}
// {{.OperationDoc}}
func {{.OperationName}}(ctx context.Context, client *graphql.Client) (*{{.ResponseName}}, error) {
// {{.Doc}}
func {{.Name}}(ctx context.Context, client *graphql.Client{{range .Args}}, {{.GoName}} {{.GoType}}{{end}}) (*{{.ResponseName}}, error) {
{{if .Args}}
variables := map[string]interface{}{
{{range .Args}}
"{{.GraphQLName}}": {{.GoName}},
{{end}}
}
{{end}}
var retval {{.ResponseName}}
err := client.MakeRequest(ctx, `{{.Operation}}`, &retval)
err := client.MakeRequest(ctx, `{{.Body}}`, &retval, {{if .Args}}variables{{else}}nil{{end}})
return &retval, err
}
{{end}}
+11 -1
View File
@@ -7,7 +7,7 @@ import (
"github.com/vektah/gqlparser/ast"
)
func typeFor(operation *ast.OperationDefinition, schema *ast.Schema) string {
func typeForOperation(operation *ast.OperationDefinition, schema *ast.Schema) string {
var builder strings.Builder
writeSelectionSetStruct(&builder, operation.SelectionSet, schema)
@@ -15,6 +15,16 @@ func typeFor(operation *ast.OperationDefinition, schema *ast.Schema) string {
return builder.String()
}
func typeForInputType(typ *ast.Type, schema *ast.Schema) string {
var builder strings.Builder
// TODO: handle non-scalar types (by passing ...something... as the
// SelectionSet?)
writeType(&builder, typ, nil, schema)
return builder.String()
}
func writeSelectionSetStruct(builder *strings.Builder, selectionSet ast.SelectionSet, schema *ast.Schema) {
builder.WriteString("struct {\n")
for _, selection := range selectionSet {
+4 -4
View File
@@ -25,14 +25,14 @@ func NewClient(endpoint string, httpClient *http.Client) *Client {
}
type payload struct {
Query string `json:"query"`
Variables map[string]string `json:"variables"`
Query string `json:"query"`
Variables map[string]interface{} `json:"variables"`
}
func (client *Client) MakeRequest(ctx context.Context, query string, retval interface{}) error {
func (client *Client) MakeRequest(ctx context.Context, query string, retval interface{}, variables map[string]interface{}) error {
body, err := json.Marshal(payload{
Query: query,
Variables: nil, // TODO
Variables: variables,
})
if err != nil {
return err