genql: quick hack at variables
This commit is contained in:
+14
-2
@@ -34,6 +34,12 @@ func Main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(os.Args) != 2 {
|
||||||
|
err = fmt.Errorf("usage: %v <username>", os.Args[0])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
username := os.Args[1]
|
||||||
|
|
||||||
httpClient := http.Client{
|
httpClient := http.Client{
|
||||||
Transport: &authedTransport{
|
Transport: &authedTransport{
|
||||||
key: key,
|
key: key,
|
||||||
@@ -41,10 +47,16 @@ func Main() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
graphqlClient := graphql.NewClient("https://api.github.com/graphql", &httpClient)
|
graphqlClient := graphql.NewClient("https://api.github.com/graphql", &httpClient)
|
||||||
resp, err := getViewer(context.Background(), graphqlClient)
|
|
||||||
|
viewerResp, err := getViewer(context.Background(), graphqlClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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
@@ -14,6 +14,7 @@ type getViewerResponse = struct {
|
|||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
func getViewer(ctx context.Context, client *graphql.Client) (*getViewerResponse, error) {
|
func getViewer(ctx context.Context, client *graphql.Client) (*getViewerResponse, error) {
|
||||||
|
|
||||||
var retval getViewerResponse
|
var retval getViewerResponse
|
||||||
err := client.MakeRequest(ctx, `
|
err := client.MakeRequest(ctx, `
|
||||||
query getViewer {
|
query getViewer {
|
||||||
@@ -21,6 +22,31 @@ query getViewer {
|
|||||||
Name: name
|
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
|
return &retval, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,3 +4,10 @@ query getViewer {
|
|||||||
Name: name
|
Name: name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# getUser gets the given user's name.
|
||||||
|
query getUser($login: String!) {
|
||||||
|
User: user(login: $login) {
|
||||||
|
Name: name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+38
-19
@@ -26,22 +26,31 @@ 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.
|
// 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.
|
// 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 type of the operation (query, mutation, or subscription).
|
}
|
||||||
OperationType ast.Operation
|
|
||||||
// The name of the operation, from GraphQL.
|
type Argument struct {
|
||||||
OperationName string
|
GoName string
|
||||||
// The documentation for the operation, from GraphQL.
|
GoType string
|
||||||
OperationDoc string
|
GraphQLName string
|
||||||
// The body of the operation to send.
|
|
||||||
Operation string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Generate(specFilename, schemaFilename, generatedFilename string) error {
|
func Generate(specFilename, schemaFilename, generatedFilename string) error {
|
||||||
@@ -98,7 +107,7 @@ func Generate(specFilename, schemaFilename, generatedFilename string) error {
|
|||||||
packageName := "example"
|
packageName := "example"
|
||||||
|
|
||||||
// TODO: this should probably get factored out
|
// 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 {
|
for i, operation := range document.Operations {
|
||||||
// TODO: we may have to actually get the precise query text, in case we
|
// 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
|
// 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},
|
Operations: ast.OperationList{operation},
|
||||||
// TODO: handle fragments
|
// TODO: handle fragments
|
||||||
})
|
})
|
||||||
operations[i] = OperationParams{
|
|
||||||
OperationType: operation.Operation,
|
args := make([]Argument, len(operation.VariableDefinitions))
|
||||||
OperationName: operation.Name,
|
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
|
// TODO: this is actually awkward, because GraphQL doesn't allow
|
||||||
// for docstrings on queries (only schemas). So we have to extract
|
// for docstrings on queries (only schemas). So we have to extract
|
||||||
// the comment, or omit doc-comments for now.
|
// 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
|
// TODO: configure ResponseName format
|
||||||
ResponseName: operation.Name + "Response",
|
ResponseName: operation.Name + "Response",
|
||||||
ResponseType: typeFor(operation, schema),
|
ResponseType: typeForOperation(operation, schema),
|
||||||
|
|
||||||
// The newline just makes it format a little nicer
|
|
||||||
Operation: "\n" + builder.String(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,17 @@ import (
|
|||||||
{{range .Operations}}
|
{{range .Operations}}
|
||||||
type {{.ResponseName}} = {{.ResponseType}}
|
type {{.ResponseName}} = {{.ResponseType}}
|
||||||
|
|
||||||
// {{.OperationDoc}}
|
// {{.Doc}}
|
||||||
func {{.OperationName}}(ctx context.Context, client *graphql.Client) (*{{.ResponseName}}, error) {
|
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}}
|
var retval {{.ResponseName}}
|
||||||
err := client.MakeRequest(ctx, `{{.Operation}}`, &retval)
|
err := client.MakeRequest(ctx, `{{.Body}}`, &retval, {{if .Args}}variables{{else}}nil{{end}})
|
||||||
return &retval, err
|
return &retval, err
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
+11
-1
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/vektah/gqlparser/ast"
|
"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
|
var builder strings.Builder
|
||||||
|
|
||||||
writeSelectionSetStruct(&builder, operation.SelectionSet, schema)
|
writeSelectionSetStruct(&builder, operation.SelectionSet, schema)
|
||||||
@@ -15,6 +15,16 @@ func typeFor(operation *ast.OperationDefinition, schema *ast.Schema) string {
|
|||||||
return builder.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) {
|
func writeSelectionSetStruct(builder *strings.Builder, selectionSet ast.SelectionSet, schema *ast.Schema) {
|
||||||
builder.WriteString("struct {\n")
|
builder.WriteString("struct {\n")
|
||||||
for _, selection := range selectionSet {
|
for _, selection := range selectionSet {
|
||||||
|
|||||||
+4
-4
@@ -25,14 +25,14 @@ func NewClient(endpoint string, httpClient *http.Client) *Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type payload struct {
|
type payload struct {
|
||||||
Query string `json:"query"`
|
Query string `json:"query"`
|
||||||
Variables map[string]string `json:"variables"`
|
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{
|
body, err := json.Marshal(payload{
|
||||||
Query: query,
|
Query: query,
|
||||||
Variables: nil, // TODO
|
Variables: variables,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user