From c19f62c2c77269a4f33c7dbc86f9553f4bf46f42 Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Fri, 17 Jan 2020 18:18:04 -0500 Subject: [PATCH] non-ridiculous handling of scalars --- generate/generate.go | 20 +++++++++++++++----- generate/types.go | 44 +++++++++++++++++++++++++++++--------------- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/generate/generate.go b/generate/generate.go index b11512e..b293ed3 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -56,7 +56,7 @@ func fromASTArg(arg *ast.VariableDefinition, schema *ast.Schema) argument { } } -func fromASTOperation(op *ast.OperationDefinition, schema *ast.Schema) operation { +func fromASTOperation(op *ast.OperationDefinition, schema *ast.Schema) (operation, error) { // 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 // there's no reasonable way to do that with several queries in one @@ -72,6 +72,12 @@ func fromASTOperation(op *ast.OperationDefinition, schema *ast.Schema) operation for i, arg := range op.VariableDefinitions { args[i] = fromASTArg(arg, schema) } + + typ, err := typeForOperation(op, schema) + if err != nil { + return operation{}, fmt.Errorf("could not compute return-type for query: %v", err) + } + return operation{ Type: op.Operation, Name: op.Name, @@ -85,14 +91,18 @@ func fromASTOperation(op *ast.OperationDefinition, schema *ast.Schema) operation // TODO: configure ResponseName format ResponseName: op.Name + "Response", - ResponseType: typeForOperation(op, schema), - } + ResponseType: typ, + }, nil } func Generate(schema *ast.Schema, document *ast.QueryDocument) ([]byte, error) { + var err error operations := make([]operation, len(document.Operations)) for i, op := range document.Operations { - operations[i] = fromASTOperation(op, schema) + operations[i], err = fromASTOperation(op, schema) + if err != nil { + return nil, err + } } data := templateParams{ @@ -102,7 +112,7 @@ func Generate(schema *ast.Schema, document *ast.QueryDocument) ([]byte, error) { } var buf bytes.Buffer - err := tmpl.Execute(&buf, data) + err = tmpl.Execute(&buf, data) if err != nil { return nil, fmt.Errorf("could not render template: %v", err) } diff --git a/generate/types.go b/generate/types.go index ffdce34..ac05a44 100644 --- a/generate/types.go +++ b/generate/types.go @@ -7,12 +7,10 @@ import ( "github.com/vektah/gqlparser/ast" ) -func typeForOperation(operation *ast.OperationDefinition, schema *ast.Schema) string { +func typeForOperation(operation *ast.OperationDefinition, schema *ast.Schema) (string, error) { var builder strings.Builder - - writeSelectionSetStruct(&builder, operation.SelectionSet, schema) - - return builder.String() + err := writeSelectionSetStruct(&builder, operation.SelectionSet, schema) + return builder.String(), err } func typeForInputType(typ *ast.Type, schema *ast.Schema) string { @@ -25,7 +23,7 @@ func typeForInputType(typ *ast.Type, schema *ast.Schema) string { 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) error { builder.WriteString("struct {\n") for _, selection := range selectionSet { switch selection := selection.(type) { @@ -52,15 +50,29 @@ func writeSelectionSetStruct(builder *strings.Builder, selectionSet ast.Selectio builder.WriteRune('\n') case *ast.FragmentSpread, *ast.InlineFragment: - panic("TODO") + return fmt.Errorf("not implemented: %T", selection) default: - panic(fmt.Errorf("invalid selection type: %v", selection)) + return fmt.Errorf("invalid selection type: %v", selection) } } builder.WriteString("}") + return nil } -func writeType(builder *strings.Builder, typ *ast.Type, selectionSet ast.SelectionSet, schema *ast.Schema) { +var graphQLNameToGoName = map[string]string{ + "Int": "int", // TODO: technically int32 is always enough, use that? + "Float": "float64", + "String": "string", + "Boolean": "bool", + "ID": "string", // TODO: named type for IDs? +} + +func writeType(builder *strings.Builder, typ *ast.Type, selectionSet ast.SelectionSet, schema *ast.Schema) error { + // gqlgen does slightly different things here since it defines names for + // all the intermediate types, but its implementation may be useful to crib + // from: + // https://github.com/99designs/gqlgen/blob/master/plugin/modelgen/models.go#L113 + // TODO: or maybe we should do that? if typ.Elem != nil { // Type is a list. builder.WriteString("[]") @@ -70,12 +82,14 @@ func writeType(builder *strings.Builder, typ *ast.Type, selectionSet ast.Selecti } if selectionSet != nil { - writeSelectionSetStruct(builder, selectionSet, schema) - return + return writeSelectionSetStruct(builder, selectionSet, schema) } - // TODO: actually handle scalars. or can we instead use gqlgen's - // converter? they're doing mostly the same thing. if not, crib from it: - // https://github.com/99designs/gqlgen/blob/master/plugin/modelgen/models.go#L113 - builder.WriteString(strings.ToLower(typ.Name())) + // TODO: handle enums better. (do unions need special handling?) + goName := graphQLNameToGoName[typ.Name()] + if goName == "" { + return fmt.Errorf("unknown scalar name: %s", typ.Name()) + } + builder.WriteString(goName) + return nil }