diff --git a/generate/generate.go b/generate/generate.go index 1ab34dad..6af6cf1d 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -224,16 +224,40 @@ func (g *generator) preprocessQueryDocument(doc *ast.QueryDocument) { validator.Walk(g.schema, doc, &observers) } +// validateOperation checks for a few classes of operations that gqlparser +// considers valid but we don't allow, and returns an error if this operation +// is invalid for genqlient's purposes. +func (g *generator) validateOperation(op *ast.OperationDefinition) error { + opType, err := g.baseTypeForOperation(op.Operation) + switch { + case err != nil: + // (e.g. operation has subscriptions, which we don't support) + return err + case opType == nil: + // gqlparser should err here, but doesn't [1], so we err to prevent + // panics later. + // TODO(benkraft): Remove once gqlparser is fixed. + // [1] https://github.com/vektah/gqlparser/issues/221 + return errorf(op.Position, "schema has no %v type", op.Operation) + } + + if op.Name == "" { + return errorf(op.Position, "operations must have operation-names") + } else if goKeywords[op.Name] { + return errorf(op.Position, "operation name must not be a go keyword") + } + + return nil +} + // addOperation adds to g.Operations the information needed to generate a // genqlient entrypoint function for the given operation. It also adds to // g.typeMap any types referenced by the operation, except for types belonging // to named fragments, which are added separately by Generate via // convertFragment. func (g *generator) addOperation(op *ast.OperationDefinition) error { - if op.Name == "" { - return errorf(op.Position, "operations must have operation-names") - } else if goKeywords[op.Name] { - return errorf(op.Position, "operation name must not be a go keyword") + if err := g.validateOperation(op); err != nil { + return err } queryDoc := &ast.QueryDocument{ diff --git a/generate/testdata/errors/NoMutationType.graphql b/generate/testdata/errors/NoMutationType.graphql new file mode 100644 index 00000000..682e969a --- /dev/null +++ b/generate/testdata/errors/NoMutationType.graphql @@ -0,0 +1 @@ +mutation M { g } diff --git a/generate/testdata/errors/NoMutationType.schema.graphql b/generate/testdata/errors/NoMutationType.schema.graphql new file mode 100644 index 00000000..75cab938 --- /dev/null +++ b/generate/testdata/errors/NoMutationType.schema.graphql @@ -0,0 +1 @@ +type Query { f: String } diff --git a/generate/testdata/errors/NoQueryType.graphql b/generate/testdata/errors/NoQueryType.graphql new file mode 100644 index 00000000..c6ce7398 --- /dev/null +++ b/generate/testdata/errors/NoQueryType.graphql @@ -0,0 +1 @@ +query Q { f } diff --git a/generate/testdata/errors/NoQueryType.schema.graphql b/generate/testdata/errors/NoQueryType.schema.graphql new file mode 100644 index 00000000..8b8f8b92 --- /dev/null +++ b/generate/testdata/errors/NoQueryType.schema.graphql @@ -0,0 +1 @@ +type T { f: String } diff --git a/generate/testdata/snapshots/TestGenerateErrors-NoMutationType-graphql b/generate/testdata/snapshots/TestGenerateErrors-NoMutationType-graphql new file mode 100644 index 00000000..2c40a67d --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateErrors-NoMutationType-graphql @@ -0,0 +1 @@ +testdata/errors/NoMutationType.graphql:1: schema has no mutation type diff --git a/generate/testdata/snapshots/TestGenerateErrors-NoQueryType-graphql b/generate/testdata/snapshots/TestGenerateErrors-NoQueryType-graphql new file mode 100644 index 00000000..aa00a565 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateErrors-NoQueryType-graphql @@ -0,0 +1 @@ +testdata/errors/NoQueryType.graphql:1: schema has no query type