diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml index 88e1e03368..af906a39ac 100644 --- a/.github/workflows/nightly.yaml +++ b/.github/workflows/nightly.yaml @@ -129,7 +129,7 @@ jobs: format: table exit-code: '1' ignore-unfixed: true - skip-dirs: vendor/,internal/gqlparser/validator/imported/ + skip-dirs: vendor/ severity: CRITICAL,HIGH env: TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db diff --git a/.golangci.yaml b/.golangci.yaml index 0d4197eec2..56bac58254 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -5,7 +5,6 @@ issues: max-same-issues: 0 # don't hide issues in CI runs because they are the same type exclude-dirs: - internal/gojsonschema - - internal/gqlparser - internal/jwx exclude-rules: - path: ast/ diff --git a/go.mod b/go.mod index 37b058cfd1..f463d0f922 100644 --- a/go.mod +++ b/go.mod @@ -35,6 +35,7 @@ require ( github.com/spf13/pflag v1.0.6 github.com/spf13/viper v1.20.1 github.com/tchap/go-patricia/v2 v2.3.2 + github.com/vektah/gqlparser/v2 v2.5.26 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 github.com/yashtewari/glob-intersection v0.2.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 diff --git a/go.sum b/go.sum index 457122b8df..1712999a87 100644 --- a/go.sum +++ b/go.sum @@ -171,6 +171,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM= github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/vektah/gqlparser/v2 v2.5.26 h1:REqqFkO8+SOEgZHR/eHScjjVjGS8Nk3RMO/juiTobN4= +github.com/vektah/gqlparser/v2 v2.5.26/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= diff --git a/internal/gqlparser/.gitignore b/internal/gqlparser/.gitignore deleted file mode 100644 index 877392a763..0000000000 --- a/internal/gqlparser/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/vendor -/validator/imported/node_modules -/validator/imported/graphql-js - -.idea/ diff --git a/internal/gqlparser/README.md b/internal/gqlparser/README.md deleted file mode 100644 index 1e58adb056..0000000000 --- a/internal/gqlparser/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# gqlparser (Details of library residing in OPA's internal) - -## Description - -https://github.com/vektah/gqlparser was duplicated into `internal/gqlparser` folder, so that we no longer have to track the external library 1-to-1, and so that OPA library users who want to use newer/older gqlparser versions won't have to match our GraphQL parser's version. - -The current version we have forked from is commit [`b3be96f` on branch `master`](https://github.com/vektah/gqlparser/commit/b3be96ff69fa97682c43570dcb6f75d08fdf8586), which is 2 commits past the [`v2.5.1`](https://github.com/vektah/gqlparser/releases/tag/v2.5.1) release tag. -We picked this specific commit because the two commits after the 2.5.1 release dramatically improve the linter state of the library to be as strict or stricter than OPA's linting, allowing for nearly drop-in integration. - -We currently modify `gqlparser/gqlerror/error.go` to provide the line and column of the error location, so as to keep our `graphql` builtin error messages consistent. -This requires either modifying all the library tests, or removing them. -For now, at least until upstream adds columns to error messages, we will just remove tests from the imported library with the `remove-tests.sh` script. - -## Rewriter script - -The `rewrite-deps.sh` can be run from this directory, and it will do the grunt work of rewriting all import path prefixes for `gqlparser` sub-packages, so that they import this package. -It also will add some linter ignore annotations on the validator rules, since those are tedious to do by hand. - -The script thus should alleviate around 40-60% of the linter-fixup work required during a version bump. - -## JSON Position Marshal script - -The `remove-position-marshal.sh` script can be executed from this directory. -This script annotates gqlparser structs to exclude the Position field during JSON marshaling. -The Position field contains a copy of the original schema for each element, potentially causing unexpectedly large memory allocations. -OPA subsequently prunes the Position from the AST, so it makes sense not to generate it in the first place. - -## Original README - -This is a parser for graphql, written to mirror the graphql-js reference implementation as closely while remaining idiomatic and easy to use. - -spec target: June 2018 (Schema definition language, block strings as descriptions, error paths & extension) - -This parser is used by [gqlgen](https://github.com/99designs/gqlgen), and it should be reasonably stable. - -Guiding principles: - - - maintainability: It should be easy to stay up to date with the spec - - well tested: It shouldn't need a graphql server to validate itself. Changes to this repo should be self contained. - - server agnostic: It should be usable by any of the graphql server implementations, and any graphql client tooling. - - idiomatic & stable api: It should follow go best practices, especially around forwards compatibility. - - fast: Where it doesn't impact on the above it should be fast. Avoid unnecessary allocs in hot paths. - - close to reference: Where it doesn't impact on the above, it should stay close to the [graphql/graphql-js](https://github.com/graphql/graphql-js) reference implementation. diff --git a/internal/gqlparser/formatter/formatter.go b/internal/gqlparser/formatter/formatter.go deleted file mode 100644 index 163e69f80f..0000000000 --- a/internal/gqlparser/formatter/formatter.go +++ /dev/null @@ -1,637 +0,0 @@ -package formatter - -import ( - "fmt" - "io" - "sort" - "strings" - - "github.com/open-policy-agent/opa/internal/gqlparser/ast" -) - -type Formatter interface { - FormatSchema(schema *ast.Schema) - FormatSchemaDocument(doc *ast.SchemaDocument) - FormatQueryDocument(doc *ast.QueryDocument) -} - -//nolint:revive // Ignore "stuttering" name format.FormatterOption -type FormatterOption func(*formatter) - -func WithIndent(indent string) FormatterOption { - return func(f *formatter) { - f.indent = indent - } -} - -func NewFormatter(w io.Writer, options ...FormatterOption) Formatter { - f := &formatter{ - indent: "\t", - writer: w, - } - for _, opt := range options { - opt(f) - } - return f -} - -type formatter struct { - writer io.Writer - - indent string - indentSize int - emitBuiltin bool - - padNext bool - lineHead bool -} - -func (f *formatter) writeString(s string) { - _, _ = f.writer.Write([]byte(s)) -} - -func (f *formatter) writeIndent() *formatter { - if f.lineHead { - f.writeString(strings.Repeat(f.indent, f.indentSize)) - } - f.lineHead = false - f.padNext = false - - return f -} - -func (f *formatter) WriteNewline() *formatter { - f.writeString("\n") - f.lineHead = true - f.padNext = false - - return f -} - -func (f *formatter) WriteWord(word string) *formatter { - if f.lineHead { - f.writeIndent() - } - if f.padNext { - f.writeString(" ") - } - f.writeString(strings.TrimSpace(word)) - f.padNext = true - - return f -} - -func (f *formatter) WriteString(s string) *formatter { - if f.lineHead { - f.writeIndent() - } - if f.padNext { - f.writeString(" ") - } - f.writeString(s) - f.padNext = false - - return f -} - -func (f *formatter) WriteDescription(s string) *formatter { - if s == "" { - return f - } - - f.WriteString(`"""`) - if ss := strings.Split(s, "\n"); len(ss) > 1 { - f.WriteNewline() - for _, s := range ss { - f.WriteString(s).WriteNewline() - } - } else { - f.WriteString(s) - } - - f.WriteString(`"""`).WriteNewline() - - return f -} - -func (f *formatter) IncrementIndent() { - f.indentSize++ -} - -func (f *formatter) DecrementIndent() { - f.indentSize-- -} - -func (f *formatter) NoPadding() *formatter { - f.padNext = false - - return f -} - -func (f *formatter) NeedPadding() *formatter { - f.padNext = true - - return f -} - -func (f *formatter) FormatSchema(schema *ast.Schema) { - if schema == nil { - return - } - - var inSchema bool - startSchema := func() { - if !inSchema { - inSchema = true - - f.WriteWord("schema").WriteString("{").WriteNewline() - f.IncrementIndent() - } - } - if schema.Query != nil && schema.Query.Name != "Query" { - startSchema() - f.WriteWord("query").NoPadding().WriteString(":").NeedPadding() - f.WriteWord(schema.Query.Name).WriteNewline() - } - if schema.Mutation != nil && schema.Mutation.Name != "Mutation" { - startSchema() - f.WriteWord("mutation").NoPadding().WriteString(":").NeedPadding() - f.WriteWord(schema.Mutation.Name).WriteNewline() - } - if schema.Subscription != nil && schema.Subscription.Name != "Subscription" { - startSchema() - f.WriteWord("subscription").NoPadding().WriteString(":").NeedPadding() - f.WriteWord(schema.Subscription.Name).WriteNewline() - } - if inSchema { - f.DecrementIndent() - f.WriteString("}").WriteNewline() - } - - directiveNames := make([]string, 0, len(schema.Directives)) - for name := range schema.Directives { - directiveNames = append(directiveNames, name) - } - sort.Strings(directiveNames) - for _, name := range directiveNames { - f.FormatDirectiveDefinition(schema.Directives[name]) - } - - typeNames := make([]string, 0, len(schema.Types)) - for name := range schema.Types { - typeNames = append(typeNames, name) - } - sort.Strings(typeNames) - for _, name := range typeNames { - f.FormatDefinition(schema.Types[name], false) - } -} - -func (f *formatter) FormatSchemaDocument(doc *ast.SchemaDocument) { - // TODO emit by position based order - - if doc == nil { - return - } - - f.FormatSchemaDefinitionList(doc.Schema, false) - f.FormatSchemaDefinitionList(doc.SchemaExtension, true) - - f.FormatDirectiveDefinitionList(doc.Directives) - - f.FormatDefinitionList(doc.Definitions, false) - f.FormatDefinitionList(doc.Extensions, true) -} - -func (f *formatter) FormatQueryDocument(doc *ast.QueryDocument) { - // TODO emit by position based order - - if doc == nil { - return - } - - f.FormatOperationList(doc.Operations) - f.FormatFragmentDefinitionList(doc.Fragments) -} - -func (f *formatter) FormatSchemaDefinitionList(lists ast.SchemaDefinitionList, extension bool) { - if len(lists) == 0 { - return - } - - if extension { - f.WriteWord("extend") - } - f.WriteWord("schema").WriteString("{").WriteNewline() - f.IncrementIndent() - - for _, def := range lists { - f.FormatSchemaDefinition(def) - } - - f.DecrementIndent() - f.WriteString("}").WriteNewline() -} - -func (f *formatter) FormatSchemaDefinition(def *ast.SchemaDefinition) { - f.WriteDescription(def.Description) - - f.FormatDirectiveList(def.Directives) - - f.FormatOperationTypeDefinitionList(def.OperationTypes) -} - -func (f *formatter) FormatOperationTypeDefinitionList(lists ast.OperationTypeDefinitionList) { - for _, def := range lists { - f.FormatOperationTypeDefinition(def) - } -} - -func (f *formatter) FormatOperationTypeDefinition(def *ast.OperationTypeDefinition) { - f.WriteWord(string(def.Operation)).NoPadding().WriteString(":").NeedPadding() - f.WriteWord(def.Type) - f.WriteNewline() -} - -func (f *formatter) FormatFieldList(fieldList ast.FieldList) { - if len(fieldList) == 0 { - return - } - - f.WriteString("{").WriteNewline() - f.IncrementIndent() - - for _, field := range fieldList { - f.FormatFieldDefinition(field) - } - - f.DecrementIndent() - f.WriteString("}") -} - -func (f *formatter) FormatFieldDefinition(field *ast.FieldDefinition) { - if !f.emitBuiltin && strings.HasPrefix(field.Name, "__") { - return - } - - f.WriteDescription(field.Description) - - f.WriteWord(field.Name).NoPadding() - f.FormatArgumentDefinitionList(field.Arguments) - f.NoPadding().WriteString(":").NeedPadding() - f.FormatType(field.Type) - - if field.DefaultValue != nil { - f.WriteWord("=") - f.FormatValue(field.DefaultValue) - } - - f.FormatDirectiveList(field.Directives) - - f.WriteNewline() -} - -func (f *formatter) FormatArgumentDefinitionList(lists ast.ArgumentDefinitionList) { - if len(lists) == 0 { - return - } - - f.WriteString("(") - for idx, arg := range lists { - f.FormatArgumentDefinition(arg) - - // Skip emitting (insignificant) comma in case it is the - // last argument, or we printed a new line in its definition. - if idx != len(lists)-1 && arg.Description == "" { - f.NoPadding().WriteWord(",") - } - } - f.NoPadding().WriteString(")").NeedPadding() -} - -func (f *formatter) FormatArgumentDefinition(def *ast.ArgumentDefinition) { - if def.Description != "" { - f.WriteNewline().IncrementIndent() - f.WriteDescription(def.Description) - } - - f.WriteWord(def.Name).NoPadding().WriteString(":").NeedPadding() - f.FormatType(def.Type) - - if def.DefaultValue != nil { - f.WriteWord("=") - f.FormatValue(def.DefaultValue) - } - - f.NeedPadding().FormatDirectiveList(def.Directives) - - if def.Description != "" { - f.DecrementIndent() - f.WriteNewline() - } -} - -func (f *formatter) FormatDirectiveLocation(location ast.DirectiveLocation) { - f.WriteWord(string(location)) -} - -func (f *formatter) FormatDirectiveDefinitionList(lists ast.DirectiveDefinitionList) { - if len(lists) == 0 { - return - } - - for _, dec := range lists { - f.FormatDirectiveDefinition(dec) - } -} - -func (f *formatter) FormatDirectiveDefinition(def *ast.DirectiveDefinition) { - if !f.emitBuiltin { - if def.Position.Src.BuiltIn { - return - } - } - - f.WriteDescription(def.Description) - f.WriteWord("directive").WriteString("@").WriteWord(def.Name) - - if len(def.Arguments) != 0 { - f.NoPadding() - f.FormatArgumentDefinitionList(def.Arguments) - } - - if len(def.Locations) != 0 { - f.WriteWord("on") - - for idx, dirLoc := range def.Locations { - f.FormatDirectiveLocation(dirLoc) - - if idx != len(def.Locations)-1 { - f.WriteWord("|") - } - } - } - - f.WriteNewline() -} - -func (f *formatter) FormatDefinitionList(lists ast.DefinitionList, extend bool) { - if len(lists) == 0 { - return - } - - for _, dec := range lists { - f.FormatDefinition(dec, extend) - } -} - -func (f *formatter) FormatDefinition(def *ast.Definition, extend bool) { - if !f.emitBuiltin && def.BuiltIn { - return - } - - f.WriteDescription(def.Description) - - if extend { - f.WriteWord("extend") - } - - switch def.Kind { - case ast.Scalar: - f.WriteWord("scalar").WriteWord(def.Name) - - case ast.Object: - f.WriteWord("type").WriteWord(def.Name) - - case ast.Interface: - f.WriteWord("interface").WriteWord(def.Name) - - case ast.Union: - f.WriteWord("union").WriteWord(def.Name) - - case ast.Enum: - f.WriteWord("enum").WriteWord(def.Name) - - case ast.InputObject: - f.WriteWord("input").WriteWord(def.Name) - } - - if len(def.Interfaces) != 0 { - f.WriteWord("implements").WriteWord(strings.Join(def.Interfaces, " & ")) - } - - f.FormatDirectiveList(def.Directives) - - if len(def.Types) != 0 { - f.WriteWord("=").WriteWord(strings.Join(def.Types, " | ")) - } - - f.FormatFieldList(def.Fields) - - f.FormatEnumValueList(def.EnumValues) - - f.WriteNewline() -} - -func (f *formatter) FormatEnumValueList(lists ast.EnumValueList) { - if len(lists) == 0 { - return - } - - f.WriteString("{").WriteNewline() - f.IncrementIndent() - - for _, v := range lists { - f.FormatEnumValueDefinition(v) - } - - f.DecrementIndent() - f.WriteString("}") -} - -func (f *formatter) FormatEnumValueDefinition(def *ast.EnumValueDefinition) { - f.WriteDescription(def.Description) - - f.WriteWord(def.Name) - f.FormatDirectiveList(def.Directives) - - f.WriteNewline() -} - -func (f *formatter) FormatOperationList(lists ast.OperationList) { - for _, def := range lists { - f.FormatOperationDefinition(def) - } -} - -func (f *formatter) FormatOperationDefinition(def *ast.OperationDefinition) { - f.WriteWord(string(def.Operation)) - if def.Name != "" { - f.WriteWord(def.Name) - } - f.FormatVariableDefinitionList(def.VariableDefinitions) - f.FormatDirectiveList(def.Directives) - - if len(def.SelectionSet) != 0 { - f.FormatSelectionSet(def.SelectionSet) - f.WriteNewline() - } -} - -func (f *formatter) FormatDirectiveList(lists ast.DirectiveList) { - if len(lists) == 0 { - return - } - - for _, dir := range lists { - f.FormatDirective(dir) - } -} - -func (f *formatter) FormatDirective(dir *ast.Directive) { - f.WriteString("@").WriteWord(dir.Name) - f.FormatArgumentList(dir.Arguments) -} - -func (f *formatter) FormatArgumentList(lists ast.ArgumentList) { - if len(lists) == 0 { - return - } - f.NoPadding().WriteString("(") - for idx, arg := range lists { - f.FormatArgument(arg) - - if idx != len(lists)-1 { - f.NoPadding().WriteWord(",") - } - } - f.WriteString(")").NeedPadding() -} - -func (f *formatter) FormatArgument(arg *ast.Argument) { - f.WriteWord(arg.Name).NoPadding().WriteString(":").NeedPadding() - f.WriteString(arg.Value.String()) -} - -func (f *formatter) FormatFragmentDefinitionList(lists ast.FragmentDefinitionList) { - for _, def := range lists { - f.FormatFragmentDefinition(def) - } -} - -func (f *formatter) FormatFragmentDefinition(def *ast.FragmentDefinition) { - f.WriteWord("fragment").WriteWord(def.Name) - f.FormatVariableDefinitionList(def.VariableDefinition) - f.WriteWord("on").WriteWord(def.TypeCondition) - f.FormatDirectiveList(def.Directives) - - if len(def.SelectionSet) != 0 { - f.FormatSelectionSet(def.SelectionSet) - f.WriteNewline() - } -} - -func (f *formatter) FormatVariableDefinitionList(lists ast.VariableDefinitionList) { - if len(lists) == 0 { - return - } - - f.WriteString("(") - for idx, def := range lists { - f.FormatVariableDefinition(def) - - if idx != len(lists)-1 { - f.NoPadding().WriteWord(",") - } - } - f.NoPadding().WriteString(")").NeedPadding() -} - -func (f *formatter) FormatVariableDefinition(def *ast.VariableDefinition) { - f.WriteString("$").WriteWord(def.Variable).NoPadding().WriteString(":").NeedPadding() - f.FormatType(def.Type) - - if def.DefaultValue != nil { - f.WriteWord("=") - f.FormatValue(def.DefaultValue) - } - - // TODO https://github.com/open-policy-agent/opa/internal/gqlparser/issues/102 - // VariableDefinition : Variable : Type DefaultValue? Directives[Const]? -} - -func (f *formatter) FormatSelectionSet(sets ast.SelectionSet) { - if len(sets) == 0 { - return - } - - f.WriteString("{").WriteNewline() - f.IncrementIndent() - - for _, sel := range sets { - f.FormatSelection(sel) - } - - f.DecrementIndent() - f.WriteString("}") -} - -func (f *formatter) FormatSelection(selection ast.Selection) { - switch v := selection.(type) { - case *ast.Field: - f.FormatField(v) - - case *ast.FragmentSpread: - f.FormatFragmentSpread(v) - - case *ast.InlineFragment: - f.FormatInlineFragment(v) - - default: - panic(fmt.Errorf("unknown Selection type: %T", selection)) - } - - f.WriteNewline() -} - -func (f *formatter) FormatField(field *ast.Field) { - if field.Alias != "" && field.Alias != field.Name { - f.WriteWord(field.Alias).NoPadding().WriteString(":").NeedPadding() - } - f.WriteWord(field.Name) - - if len(field.Arguments) != 0 { - f.NoPadding() - f.FormatArgumentList(field.Arguments) - f.NeedPadding() - } - - f.FormatDirectiveList(field.Directives) - - f.FormatSelectionSet(field.SelectionSet) -} - -func (f *formatter) FormatFragmentSpread(spread *ast.FragmentSpread) { - f.WriteWord("...").WriteWord(spread.Name) - - f.FormatDirectiveList(spread.Directives) -} - -func (f *formatter) FormatInlineFragment(inline *ast.InlineFragment) { - f.WriteWord("...") - if inline.TypeCondition != "" { - f.WriteWord("on").WriteWord(inline.TypeCondition) - } - - f.FormatDirectiveList(inline.Directives) - - f.FormatSelectionSet(inline.SelectionSet) -} - -func (f *formatter) FormatType(t *ast.Type) { - f.WriteWord(t.String()) -} - -func (f *formatter) FormatValue(value *ast.Value) { - f.WriteString(value.String()) -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/basic.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/basic.graphql deleted file mode 100644 index 238d422e00..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/basic.graphql +++ /dev/null @@ -1,7 +0,0 @@ -query FooBarQuery ($after: String!) { - fizzList(first: 100, after: $after) { - nodes { - id - } - } -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/field.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/field.graphql deleted file mode 100644 index 7614c27ecf..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/field.graphql +++ /dev/null @@ -1,3 +0,0 @@ -query { - bar: foo -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/fragment.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/fragment.graphql deleted file mode 100644 index 6ecda70909..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/fragment.graphql +++ /dev/null @@ -1,18 +0,0 @@ -query FooBarQuery ($after: String!) { - fizzList(first: 100, after: $after) { - nodes { - id - ... FooFragment - ... on Foo { - id - } - ... { - id - } - name - } - } -} -fragment FooFragment on Foo { - id -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/variable.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/variable.graphql deleted file mode 100644 index 3eeec53b1c..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatQueryDocument/variable.graphql +++ /dev/null @@ -1,8 +0,0 @@ -query ($first: Int = 30, $after: String!) { - searchCats(first: $first, after: $after) { - nodes { - id - name - } - } -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/definition.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchema/definition.graphql deleted file mode 100644 index ac651492c3..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/definition.graphql +++ /dev/null @@ -1,24 +0,0 @@ -scalar Cat0 -type Cat1 { - name: String -} -interface Cat2 { - name: String -} -union Cat3 = Cat3_0 | Cat3_1 | Cat3_2 -type Cat3_0 { - name: String -} -type Cat3_1 { - name: String -} -type Cat3_2 { - name: String -} -enum Cat4 { - NFC - MAINECOON -} -input Cat5 { - name: String -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/description.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchema/description.graphql deleted file mode 100644 index a4dca23fd4..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/description.graphql +++ /dev/null @@ -1,8 +0,0 @@ -""" -Cat is best kawaii animal in the world. -meow! -""" -type Cat { - """Shiny brillian name.""" - name: String -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/directive.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchema/directive.graphql deleted file mode 100644 index 5f65049029..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/directive.graphql +++ /dev/null @@ -1 +0,0 @@ -directive @foo on FIELD | OBJECT diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/directive_locations.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchema/directive_locations.graphql deleted file mode 100644 index 31bf49ec37..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/directive_locations.graphql +++ /dev/null @@ -1,13 +0,0 @@ -directive @foo on OBJECT | UNION | ENUM -enum ConnectionStatus @foo { - ONLINE - OFFLINE - ERROR -} -interface Named { - name: String! -} -type Person implements Named @foo { - name: String! -} -union PersonUnion @foo = Person diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/extensions.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchema/extensions.graphql deleted file mode 100644 index 813b4d0302..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/extensions.graphql +++ /dev/null @@ -1,16 +0,0 @@ -directive @extends on OBJECT -directive @key(fields: String!) on OBJECT | INTERFACE -directive @permission(permission: String!) on FIELD_DEFINITION -type Dog { - name: String! - owner: Person! @permission(permission: "admin") -} -type Person @key(fields: "name") { - name: String! -} -type Query @extends { - dogs: [Dog!]! -} -type Subscription { - dogEvents: [Dog!]! -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/field_definition.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchema/field_definition.graphql deleted file mode 100644 index 4f2a9af7f7..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/field_definition.graphql +++ /dev/null @@ -1,3 +0,0 @@ -input CatInput { - food: String = "fish & meat" -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/schema.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchema/schema.graphql deleted file mode 100644 index 1f6578be2c..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/schema.graphql +++ /dev/null @@ -1,41 +0,0 @@ -schema { - query: TopQuery - mutation: TopMutation - subscription: TopSubscription -} -type TopMutation { - noop: Boolean - noop2( - """noop2 foo bar""" - arg: String - ): Boolean - noop3( - """noop3 foo bar""" - arg: String - ): Boolean -} -type TopQuery { - noop: Boolean - noop2( - """noop2 foo bar""" - arg: String - ): Boolean - noop3( - """noop3 foo bar""" - arg: String - ): Boolean -} -type TopSubscription { - noop: Boolean - noop2( - """noop2 foo bar""" - arg: String - ): Boolean - noop3( - """noop3 foo bar""" - arg1: String - - """noop3 foo bar""" - arg2: String - ): Boolean -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/swapi.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchema/swapi.graphql deleted file mode 100644 index f2c1aea371..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchema/swapi.graphql +++ /dev/null @@ -1,87 +0,0 @@ -interface Character { - id: ID! - name: String! - friends: [Character] - friendsConnection(first: Int, after: ID): FriendsConnection! - appearsIn: [Episode]! -} -input ColorInput { - red: Int! - green: Int! - blue: Int! -} -type Droid implements Character { - id: ID! - name: String! - friends: [Character] - friendsConnection(first: Int, after: ID): FriendsConnection! - appearsIn: [Episode]! - primaryFunction: String -} -enum Episode { - NEWHOPE - EMPIRE - JEDI -} -type FriendsConnection { - totalCount: Int - edges: [FriendsEdge] - friends: [Character] - pageInfo: PageInfo! -} -type FriendsEdge { - cursor: ID! - node: Character -} -type Human implements Character { - id: ID! - name: String! - homePlanet: String - height(unit: LengthUnit = METER): Float - mass: Float - friends: [Character] - friendsConnection(first: Int, after: ID): FriendsConnection! - appearsIn: [Episode]! - starships: [Starship] -} -enum LengthUnit { - METER - FOOT -} -type Mutation { - createReview(episode: Episode, review: ReviewInput!): Review -} -type PageInfo { - startCursor: ID - endCursor: ID - hasNextPage: Boolean! -} -type Query { - hero(episode: Episode): Character - reviews(episode: Episode!): [Review] - search(text: String): [SearchResult] - character(id: ID!): Character - droid(id: ID!): Droid - human(id: ID!): Human - starship(id: ID!): Starship -} -type Review { - episode: Episode - stars: Int! - commentary: String -} -input ReviewInput { - stars: Int! - commentary: String - favorite_color: ColorInput -} -union SearchResult = Human | Droid | Starship -type Starship { - id: ID! - name: String! - length(unit: LengthUnit = METER): Float - coordinates: [[Float!]!] -} -type Subscription { - reviewAdded(episode: Episode): Review -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/definition.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/definition.graphql deleted file mode 100644 index 2eb48e32c6..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/definition.graphql +++ /dev/null @@ -1,24 +0,0 @@ -scalar Cat0 -type Cat1 { - name: String -} -interface Cat2 { - name: String -} -type Cat3_0 { - name: String -} -type Cat3_1 { - name: String -} -type Cat3_2 { - name: String -} -union Cat3 = Cat3_0 | Cat3_1 | Cat3_2 -enum Cat4 { - NFC - MAINECOON -} -input Cat5 { - name: String -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/description.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/description.graphql deleted file mode 100644 index a4dca23fd4..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/description.graphql +++ /dev/null @@ -1,8 +0,0 @@ -""" -Cat is best kawaii animal in the world. -meow! -""" -type Cat { - """Shiny brillian name.""" - name: String -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/directive.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/directive.graphql deleted file mode 100644 index 5f65049029..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/directive.graphql +++ /dev/null @@ -1 +0,0 @@ -directive @foo on FIELD | OBJECT diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/directive_locations.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/directive_locations.graphql deleted file mode 100644 index 26bd869dac..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/directive_locations.graphql +++ /dev/null @@ -1,13 +0,0 @@ -directive @foo on OBJECT | UNION | ENUM -interface Named { - name: String! -} -type Person implements Named @foo { - name: String! -} -enum ConnectionStatus @foo { - ONLINE - OFFLINE - ERROR -} -union PersonUnion @foo = Person diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/extensions.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/extensions.graphql deleted file mode 100644 index 225694f842..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/extensions.graphql +++ /dev/null @@ -1,24 +0,0 @@ -schema { - query: Query -} -extend schema { - subscription: Subscription -} -directive @permission(permission: String!) on FIELD_DEFINITION -directive @extends on OBJECT -directive @key(fields: String!) on OBJECT | INTERFACE -type Query @extends { - dogs: [Dog!]! -} -type Subscription { - dogEvents: [Dog!]! -} -type Dog { - name: String! -} -type Person @key(fields: "name") { - name: String! -} -extend type Dog { - owner: Person! @permission(permission: "admin") -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/field_definition.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/field_definition.graphql deleted file mode 100644 index 4f2a9af7f7..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/field_definition.graphql +++ /dev/null @@ -1,3 +0,0 @@ -input CatInput { - food: String = "fish & meat" -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/schema.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/schema.graphql deleted file mode 100644 index 1f6578be2c..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/schema.graphql +++ /dev/null @@ -1,41 +0,0 @@ -schema { - query: TopQuery - mutation: TopMutation - subscription: TopSubscription -} -type TopMutation { - noop: Boolean - noop2( - """noop2 foo bar""" - arg: String - ): Boolean - noop3( - """noop3 foo bar""" - arg: String - ): Boolean -} -type TopQuery { - noop: Boolean - noop2( - """noop2 foo bar""" - arg: String - ): Boolean - noop3( - """noop3 foo bar""" - arg: String - ): Boolean -} -type TopSubscription { - noop: Boolean - noop2( - """noop2 foo bar""" - arg: String - ): Boolean - noop3( - """noop3 foo bar""" - arg1: String - - """noop3 foo bar""" - arg2: String - ): Boolean -} diff --git a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/swapi.graphql b/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/swapi.graphql deleted file mode 100644 index 357f8f57ab..0000000000 --- a/internal/gqlparser/formatter/testdata/baseline/FormatSchemaDocument/swapi.graphql +++ /dev/null @@ -1,92 +0,0 @@ -schema { - query: Query - mutation: Mutation - subscription: Subscription -} -type Query { - hero(episode: Episode): Character - reviews(episode: Episode!): [Review] - search(text: String): [SearchResult] - character(id: ID!): Character - droid(id: ID!): Droid - human(id: ID!): Human - starship(id: ID!): Starship -} -type Mutation { - createReview(episode: Episode, review: ReviewInput!): Review -} -type Subscription { - reviewAdded(episode: Episode): Review -} -enum Episode { - NEWHOPE - EMPIRE - JEDI -} -interface Character { - id: ID! - name: String! - friends: [Character] - friendsConnection(first: Int, after: ID): FriendsConnection! - appearsIn: [Episode]! -} -enum LengthUnit { - METER - FOOT -} -type Human implements Character { - id: ID! - name: String! - homePlanet: String - height(unit: LengthUnit = METER): Float - mass: Float - friends: [Character] - friendsConnection(first: Int, after: ID): FriendsConnection! - appearsIn: [Episode]! - starships: [Starship] -} -type Droid implements Character { - id: ID! - name: String! - friends: [Character] - friendsConnection(first: Int, after: ID): FriendsConnection! - appearsIn: [Episode]! - primaryFunction: String -} -type FriendsConnection { - totalCount: Int - edges: [FriendsEdge] - friends: [Character] - pageInfo: PageInfo! -} -type FriendsEdge { - cursor: ID! - node: Character -} -type PageInfo { - startCursor: ID - endCursor: ID - hasNextPage: Boolean! -} -type Review { - episode: Episode - stars: Int! - commentary: String -} -input ReviewInput { - stars: Int! - commentary: String - favorite_color: ColorInput -} -input ColorInput { - red: Int! - green: Int! - blue: Int! -} -type Starship { - id: ID! - name: String! - length(unit: LengthUnit = METER): Float - coordinates: [[Float!]!] -} -union SearchResult = Human | Droid | Starship diff --git a/internal/gqlparser/formatter/testdata/source/query/basic.graphql b/internal/gqlparser/formatter/testdata/source/query/basic.graphql deleted file mode 100644 index 658ddf0b5b..0000000000 --- a/internal/gqlparser/formatter/testdata/source/query/basic.graphql +++ /dev/null @@ -1,7 +0,0 @@ -query FooBarQuery ($after: String!) { - fizzList(first: 100, after: $after) { - nodes { - id - } - } -} diff --git a/internal/gqlparser/formatter/testdata/source/query/field.graphql b/internal/gqlparser/formatter/testdata/source/query/field.graphql deleted file mode 100644 index 7ac16091ee..0000000000 --- a/internal/gqlparser/formatter/testdata/source/query/field.graphql +++ /dev/null @@ -1,3 +0,0 @@ -{ - bar: foo -} diff --git a/internal/gqlparser/formatter/testdata/source/query/fragment.graphql b/internal/gqlparser/formatter/testdata/source/query/fragment.graphql deleted file mode 100644 index 48fdc92999..0000000000 --- a/internal/gqlparser/formatter/testdata/source/query/fragment.graphql +++ /dev/null @@ -1,19 +0,0 @@ -query FooBarQuery ($after: String!) { - fizzList(first: 100, after: $after) { - nodes { - id - ... FooFragment - ... on Foo { - id - } - ... { - id - } - name - } - } -} - -fragment FooFragment on Foo { - id -} diff --git a/internal/gqlparser/formatter/testdata/source/query/variable.graphql b/internal/gqlparser/formatter/testdata/source/query/variable.graphql deleted file mode 100644 index ccea84aaff..0000000000 --- a/internal/gqlparser/formatter/testdata/source/query/variable.graphql +++ /dev/null @@ -1,8 +0,0 @@ -query ($first: Int = 30, $after: String!) { - searchCats(first: $first, after: $after) { - nodes { - id - name - } - } -} diff --git a/internal/gqlparser/formatter/testdata/source/schema/definition.graphql b/internal/gqlparser/formatter/testdata/source/schema/definition.graphql deleted file mode 100644 index 349d72f360..0000000000 --- a/internal/gqlparser/formatter/testdata/source/schema/definition.graphql +++ /dev/null @@ -1,25 +0,0 @@ -scalar Cat0 -type Cat1 { - name: String -} -interface Cat2 { - name: String -} -type Cat3_0 { - name: String -} -type Cat3_1 { - name: String -} -type Cat3_2 { - name: String -} -union Cat3 = Cat3_0|Cat3_1|Cat3_2 -enum Cat4 { - NFC - MAINECOON -} -input Cat5 { - name: String -} - diff --git a/internal/gqlparser/formatter/testdata/source/schema/description.graphql b/internal/gqlparser/formatter/testdata/source/schema/description.graphql deleted file mode 100644 index d25aff8643..0000000000 --- a/internal/gqlparser/formatter/testdata/source/schema/description.graphql +++ /dev/null @@ -1,8 +0,0 @@ -""" -Cat is best kawaii animal in the world. -meow! -""" -type Cat { - """Shiny brillian name.""" - name: String -} diff --git a/internal/gqlparser/formatter/testdata/source/schema/directive.graphql b/internal/gqlparser/formatter/testdata/source/schema/directive.graphql deleted file mode 100644 index 4b741df396..0000000000 --- a/internal/gqlparser/formatter/testdata/source/schema/directive.graphql +++ /dev/null @@ -1 +0,0 @@ -directive @foo on FIELD|OBJECT diff --git a/internal/gqlparser/formatter/testdata/source/schema/directive_locations.graphql b/internal/gqlparser/formatter/testdata/source/schema/directive_locations.graphql deleted file mode 100644 index 26bd869dac..0000000000 --- a/internal/gqlparser/formatter/testdata/source/schema/directive_locations.graphql +++ /dev/null @@ -1,13 +0,0 @@ -directive @foo on OBJECT | UNION | ENUM -interface Named { - name: String! -} -type Person implements Named @foo { - name: String! -} -enum ConnectionStatus @foo { - ONLINE - OFFLINE - ERROR -} -union PersonUnion @foo = Person diff --git a/internal/gqlparser/formatter/testdata/source/schema/extensions.graphql b/internal/gqlparser/formatter/testdata/source/schema/extensions.graphql deleted file mode 100644 index d6ee0ceb65..0000000000 --- a/internal/gqlparser/formatter/testdata/source/schema/extensions.graphql +++ /dev/null @@ -1,31 +0,0 @@ -schema { - query: Query -} - -extend schema { - subscription: Subscription -} - -type Query @extends { - dogs: [Dog!]! -} - -type Subscription { - dogEvents: [Dog!]! -} - -type Dog { - name: String! -} - -type Person @key(fields: "name") { - name: String! -} - -extend type Dog { - owner: Person! @permission(permission: "admin") -} - -directive @permission(permission: String!) on FIELD_DEFINITION -directive @extends on OBJECT -directive @key(fields: String!) on OBJECT | INTERFACE \ No newline at end of file diff --git a/internal/gqlparser/formatter/testdata/source/schema/field_definition.graphql b/internal/gqlparser/formatter/testdata/source/schema/field_definition.graphql deleted file mode 100644 index a0b26dda46..0000000000 --- a/internal/gqlparser/formatter/testdata/source/schema/field_definition.graphql +++ /dev/null @@ -1,3 +0,0 @@ -input CatInput { - food: String = "fish & meat" -} diff --git a/internal/gqlparser/formatter/testdata/source/schema/schema.graphql b/internal/gqlparser/formatter/testdata/source/schema/schema.graphql deleted file mode 100644 index 96dfc897fb..0000000000 --- a/internal/gqlparser/formatter/testdata/source/schema/schema.graphql +++ /dev/null @@ -1,49 +0,0 @@ -schema { - query: TopQuery - mutation: TopMutation - subscription: TopSubscription -} - -type TopMutation { - noop: Boolean - noop2(""" - noop2 foo bar - """ - arg: String - ): Boolean - noop3("noop3 foo bar" - arg: String - ): Boolean -} - -type TopQuery { - noop: Boolean - - noop2(""" - noop2 foo bar - """ - arg: String - ): Boolean - - noop3( - "noop3 foo bar" - arg: String - ): Boolean -} - -type TopSubscription { - noop: Boolean - - noop2( - """noop2 foo bar""" - arg: String - ): Boolean - - noop3( - "noop3 foo bar" - arg1: String - - "noop3 foo bar" - arg2: String - ): Boolean -} diff --git a/internal/gqlparser/formatter/testdata/source/schema/swapi.graphql b/internal/gqlparser/formatter/testdata/source/schema/swapi.graphql deleted file mode 100644 index 38422b7f71..0000000000 --- a/internal/gqlparser/formatter/testdata/source/schema/swapi.graphql +++ /dev/null @@ -1,147 +0,0 @@ -schema { - query: Query - mutation: Mutation - subscription: Subscription -} - -# The query type, represents all of the entry points into our object graph -type Query { - hero(episode: Episode): Character - reviews(episode: Episode!): [Review] - search(text: String): [SearchResult] - character(id: ID!): Character - droid(id: ID!): Droid - human(id: ID!): Human - starship(id: ID!): Starship -} -# The mutation type, represents all updates we can make to our data -type Mutation { - createReview(episode: Episode, review: ReviewInput!): Review -} -# The subscription type, represents all subscriptions we can make to our data -type Subscription { - reviewAdded(episode: Episode): Review -} -# The episodes in the Star Wars trilogy -enum Episode { - # Star Wars Episode IV: A New Hope, released in 1977. - NEWHOPE - # Star Wars Episode V: The Empire Strikes Back, released in 1980. - EMPIRE - # Star Wars Episode VI: Return of the Jedi, released in 1983. - JEDI -} -# A character from the Star Wars universe -interface Character { - # The ID of the character - id: ID! - # The name of the character - name: String! - # The friends of the character, or an empty list if they have none - friends: [Character] - # The friends of the character exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this character appears in - appearsIn: [Episode]! -} -# Units of height -enum LengthUnit { - # The standard unit around the world - METER - # Primarily used in the United States - FOOT -} -# A humanoid creature from the Star Wars universe -type Human implements Character { - # The ID of the human - id: ID! - # What this human calls themselves - name: String! - # The home planet of the human, or null if unknown - homePlanet: String - # Height in the preferred unit, default is meters - height(unit: LengthUnit = METER): Float - # Mass in kilograms, or null if unknown - mass: Float - # This human's friends, or an empty list if they have none - friends: [Character] - # The friends of the human exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this human appears in - appearsIn: [Episode]! - # A list of starships this person has piloted, or an empty list if none - starships: [Starship] -} -# An autonomous mechanical character in the Star Wars universe -type Droid implements Character { - # The ID of the droid - id: ID! - # What others call this droid - name: String! - # This droid's friends, or an empty list if they have none - friends: [Character] - # The friends of the droid exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this droid appears in - appearsIn: [Episode]! - # This droid's primary function - primaryFunction: String -} -# A connection object for a character's friends -type FriendsConnection { - # The total number of friends - totalCount: Int - # The edges for each of the character's friends. - edges: [FriendsEdge] - # A list of the friends, as a convenience when edges are not needed. - friends: [Character] - # Information for paginating this connection - pageInfo: PageInfo! -} -# An edge object for a character's friends -type FriendsEdge { - # A cursor used for pagination - cursor: ID! - # The character represented by this friendship edge - node: Character -} -# Information for paginating this connection -type PageInfo { - startCursor: ID - endCursor: ID - hasNextPage: Boolean! -} -# Represents a review for a movie -type Review { - # The movie - episode: Episode - # The number of stars this review gave, 1-5 - stars: Int! - # Comment about the movie - commentary: String -} -# The input object sent when someone is creating a new review -input ReviewInput { - # 0-5 stars - stars: Int! - # Comment about the movie, optional - commentary: String - # Favorite color, optional - favorite_color: ColorInput -} -# The input object sent when passing in a color -input ColorInput { - red: Int! - green: Int! - blue: Int! -} -type Starship { - # The ID of the starship - id: ID! - # The name of the starship - name: String! - # Length of the starship, along the longest axis - length(unit: LengthUnit = METER): Float - coordinates: [[Float!]!] -} -union SearchResult = Human | Droid | Starship \ No newline at end of file diff --git a/internal/gqlparser/gqlparser.go b/internal/gqlparser/gqlparser.go deleted file mode 100644 index 575f5be5e1..0000000000 --- a/internal/gqlparser/gqlparser.go +++ /dev/null @@ -1,45 +0,0 @@ -package gqlparser - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - "github.com/open-policy-agent/opa/internal/gqlparser/gqlerror" - "github.com/open-policy-agent/opa/internal/gqlparser/parser" - "github.com/open-policy-agent/opa/internal/gqlparser/validator" - - // Blank import is used to load up the validator rules. - _ "github.com/open-policy-agent/opa/internal/gqlparser/validator/rules" -) - -func LoadSchema(str ...*ast.Source) (*ast.Schema, error) { - return validator.LoadSchema(append([]*ast.Source{validator.Prelude}, str...)...) -} - -func MustLoadSchema(str ...*ast.Source) *ast.Schema { - s, err := validator.LoadSchema(append([]*ast.Source{validator.Prelude}, str...)...) - if err != nil { - panic(err) - } - return s -} - -func LoadQuery(schema *ast.Schema, str string) (*ast.QueryDocument, gqlerror.List) { - query, err := parser.ParseQuery(&ast.Source{Input: str}) - if err != nil { - gqlErr := err.(*gqlerror.Error) - return nil, gqlerror.List{gqlErr} - } - errs := validator.Validate(schema, query) - if errs != nil { - return nil, errs - } - - return query, nil -} - -func MustLoadQuery(schema *ast.Schema, str string) *ast.QueryDocument { - q, err := LoadQuery(schema, str) - if err != nil { - panic(err) - } - return q -} diff --git a/internal/gqlparser/parser/testrunner/runner.go b/internal/gqlparser/parser/testrunner/runner.go deleted file mode 100644 index 7349ec78eb..0000000000 --- a/internal/gqlparser/parser/testrunner/runner.go +++ /dev/null @@ -1,139 +0,0 @@ -package testrunner - -import ( - "os" - "strconv" - "strings" - "testing" - - "github.com/andreyvit/diff" - "github.com/open-policy-agent/opa/internal/gqlparser/gqlerror" - "gopkg.in/yaml.v3" -) - -type Features map[string][]Spec - -type Spec struct { - Name string - Input string - Error *gqlerror.Error - Tokens []Token - AST string -} - -type Token struct { - Kind string - Value string - Start int - End int - Line int - Column int - Src string -} - -func (t Token) String() string { - return t.Kind + " " + strconv.Quote(t.Value) -} - -func Test(t *testing.T, filename string, f func(t *testing.T, input string) Spec) { - b, err := os.ReadFile(filename) - if err != nil { - panic(err) - } - var tests Features - err = yaml.Unmarshal(b, &tests) - if err != nil { - t.Errorf("unable to load %s: %s", filename, err.Error()) - return - } - - for name, specs := range tests { - t.Run(name, func(t *testing.T) { - for _, spec := range specs { - t.Run(spec.Name, func(t *testing.T) { - result := f(t, spec.Input) - - if spec.Error == nil { - if result.Error != nil { - gqlErr := err.(*gqlerror.Error) - t.Errorf("unexpected error %s", gqlErr.Message) - } - } else if result.Error == nil { - t.Errorf("expected error but got none") - } else { - if result.Error.Message != spec.Error.Message { - t.Errorf("wrong error returned\nexpected: %s\ngot: %s", spec.Error.Message, result.Error.Message) - } - - if result.Error.Locations[0].Column != spec.Error.Locations[0].Column || result.Error.Locations[0].Line != spec.Error.Locations[0].Line { - t.Errorf( - "wrong error location:\nexpected: line %d column %d\ngot: line %d column %d", - spec.Error.Locations[0].Line, - spec.Error.Locations[0].Column, - result.Error.Locations[0].Line, - result.Error.Locations[0].Column, - ) - } - } - - if len(spec.Tokens) != len(result.Tokens) { - var tokensStr []string - for _, t := range result.Tokens { - tokensStr = append(tokensStr, t.String()) - } - t.Errorf("token count mismatch, got: \n%s", strings.Join(tokensStr, "\n")) - } else { - for i, tok := range result.Tokens { - expected := spec.Tokens[i] - - if !strings.EqualFold(strings.ReplaceAll(expected.Kind, "_", ""), tok.Kind) { - t.Errorf("token[%d].kind should be %s, was %s", i, expected.Kind, tok.Kind) - } - if expected.Value != "undefined" && expected.Value != tok.Value { - t.Errorf("token[%d].value incorrect\nexpected: %s\ngot: %s", i, strconv.Quote(expected.Value), strconv.Quote(tok.Value)) - } - if expected.Start != 0 && expected.Start != tok.Start { - t.Errorf("token[%d].start should be %d, was %d", i, expected.Start, tok.Start) - } - if expected.End != 0 && expected.End != tok.End { - t.Errorf("token[%d].end should be %d, was %d", i, expected.End, tok.End) - } - if expected.Line != 0 && expected.Line != tok.Line { - t.Errorf("token[%d].line should be %d, was %d", i, expected.Line, tok.Line) - } - if expected.Column != 0 && expected.Column != tok.Column { - t.Errorf("token[%d].column should be %d, was %d", i, expected.Column, tok.Column) - } - if tok.Src != "spec" { - t.Errorf("token[%d].source.name should be spec, was %s", i, strconv.Quote(tok.Src)) - } - } - } - - spec.AST = strings.TrimSpace(spec.AST) - result.AST = strings.TrimSpace(result.AST) - - if spec.AST != "" && spec.AST != result.AST { - diff := diff.LineDiff(spec.AST, result.AST) - if diff != "" { - t.Errorf("AST mismatch:\n%s", diff) - } - } - - if t.Failed() { - t.Logf("input: %s", strconv.Quote(spec.Input)) - if result.Error != nil { - t.Logf("error: %s", result.Error.Message) - } - t.Log("tokens: ") - for _, tok := range result.Tokens { - t.Logf(" - %s", tok.String()) - } - t.Logf(" - ") - } - }) - } - }) - } - -} diff --git a/internal/gqlparser/remove-position-marshal.sh b/internal/gqlparser/remove-position-marshal.sh deleted file mode 100755 index 418e4e8894..0000000000 --- a/internal/gqlparser/remove-position-marshal.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Relies on perl to do in-place regex search-and-replace for the Position annotations -# Relies on find to recursively enumerate all the Go files. - -# Add Position json annotation -# Position information is pruned by pruneIrrelevantGraphQLASTNodes() later anyway -for f in $(find . -name "*.go"); do perl -pi -e 's/\*Position \`dump:"-"\`/\*Position \`dump:"-" json:"-"\`/g' $f; done diff --git a/internal/gqlparser/remove-tests.sh b/internal/gqlparser/remove-tests.sh deleted file mode 100755 index 13080ccce6..0000000000 --- a/internal/gqlparser/remove-tests.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Remove tests from library. -find . -name "*_test.go" -delete diff --git a/internal/gqlparser/rewrite-deps.sh b/internal/gqlparser/rewrite-deps.sh deleted file mode 100755 index 77d1f6163f..0000000000 --- a/internal/gqlparser/rewrite-deps.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Relies on perl to do in-place regex search-and-replace for the module strings. -# Relies on find to recursively enumerate all the Go files. - -# Rewrite imports to use this module. -for f in $(find . -name "*.go"); do perl -pi -e "s/github.com\/vektah\/gqlparser\/v2/github.com\/open-policy-agent\/opa\/internal\/gqlparser/" $f; done diff --git a/internal/gqlparser/validator/imported/deviations.yml b/internal/gqlparser/validator/imported/deviations.yml deleted file mode 100644 index a29b7d4550..0000000000 --- a/internal/gqlparser/validator/imported/deviations.yml +++ /dev/null @@ -1,29 +0,0 @@ -- rule: 'ExecutableDefinitionsRule/.*' - skip: "These are impossible to generate because the parser is split between Query and Schema" - -- rule: 'FieldsOnCorrectType/Defined on implementors but not on interface' - errors: - - message: Cannot query field "nickname" on type "Pet". Did you mean to use an inline fragment on "Cat" or "Dog"? - locations: - - {line: 3, column: 9} - -- rule: 'KnownDirectivesRule/within schema language/with misplaced directives' - skip: "When the syntax of schema is mixed in query, parser can't consume schema syntax and ignore it" - -- rule: 'KnownTypeNamesRule/ignores type definitions' - skip: "When the syntax of schema is mixed in query, parser can't consume schema syntax and ignore it" - -- rule: 'OverlappingFieldsCanBeMergedRule/return types must be unambiguous/reports correctly when a non-exclusive follows an exclusive' - skip: "Spec issue? scalar is not exists on SomeBox" - -- rule: 'ValuesOfCorrectTypeRule/.*custom scalar.*' - skip: "Custom scalars are a runtime feature, maybe they dont belong in here?" - -- rule: 'NoDeprecatedCustomRule/.*' - skip: "This rule is optional and is not part of the Validation section of the GraphQL Specification" - -- rule: 'NoSchemaIntrospectionCustomRule/.*' - skip: "This rule is optional and is not part of the Validation section of the GraphQL Specification" - -- rule: 'KnownTypeNamesRule/references to standard scalars that are missing in schema' - skip: "standard scalars must be exists in schema" \ No newline at end of file diff --git a/internal/gqlparser/validator/imported/spec/ExecutableDefinitionsRule.spec.yml b/internal/gqlparser/validator/imported/spec/ExecutableDefinitionsRule.spec.yml deleted file mode 100644 index 8286c9645e..0000000000 --- a/internal/gqlparser/validator/imported/spec/ExecutableDefinitionsRule.spec.yml +++ /dev/null @@ -1,80 +0,0 @@ -- name: with only operation - rule: ExecutableDefinitions - schema: 0 - query: |2- - - query Foo { - dog { - name - } - } - - errors: [] -- name: with operation and fragment - rule: ExecutableDefinitions - schema: 0 - query: |2- - - query Foo { - dog { - name - ...Frag - } - } - - fragment Frag on Dog { - name - } - - errors: [] -- name: with type definition - rule: ExecutableDefinitions - schema: 0 - query: |2- - - query Foo { - dog { - name - } - } - - type Cow { - name: String - } - - extend type Dog { - color: String - } - - errors: - - message: The "Cow" definition is not executable. - locations: - - {line: 8, column: 7} - - message: The "Dog" definition is not executable. - locations: - - {line: 12, column: 7} -- name: with schema definition - rule: ExecutableDefinitions - schema: 0 - query: |2- - - schema { - query: Query - } - - type Query { - test: String - } - - extend schema @directive - - errors: - - message: The schema definition is not executable. - locations: - - {line: 2, column: 7} - - message: The "Query" definition is not executable. - locations: - - {line: 6, column: 7} - - message: The schema definition is not executable. - locations: - - {line: 10, column: 7} diff --git a/internal/gqlparser/validator/imported/spec/FieldsOnCorrectTypeRule.spec.yml b/internal/gqlparser/validator/imported/spec/FieldsOnCorrectTypeRule.spec.yml deleted file mode 100644 index a582cb770b..0000000000 --- a/internal/gqlparser/validator/imported/spec/FieldsOnCorrectTypeRule.spec.yml +++ /dev/null @@ -1,244 +0,0 @@ -- name: Object field selection - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment objectFieldSelection on Dog { - __typename - name - } - - errors: [] -- name: Aliased object field selection - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment aliasedObjectFieldSelection on Dog { - tn : __typename - otherName : name - } - - errors: [] -- name: Interface field selection - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment interfaceFieldSelection on Pet { - __typename - name - } - - errors: [] -- name: Aliased interface field selection - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment interfaceFieldSelection on Pet { - otherName : name - } - - errors: [] -- name: Lying alias selection - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment lyingAliasSelection on Dog { - name : nickname - } - - errors: [] -- name: Ignores fields on unknown type - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment unknownSelection on UnknownType { - unknownField - } - - errors: [] -- name: reports errors when type is known again - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment typeKnownAgain on Pet { - unknown_pet_field { - ... on Cat { - unknown_cat_field - } - } - } - - errors: - - message: Cannot query field "unknown_pet_field" on type "Pet". - locations: - - {line: 3, column: 9} - - message: Cannot query field "unknown_cat_field" on type "Cat". - locations: - - {line: 5, column: 13} -- name: Field not defined on fragment - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment fieldNotDefined on Dog { - meowVolume - } - - errors: - - message: Cannot query field "meowVolume" on type "Dog". Did you mean "barkVolume"? - locations: - - {line: 3, column: 9} -- name: Ignores deeply unknown field - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment deepFieldNotDefined on Dog { - unknown_field { - deeper_unknown_field - } - } - - errors: - - message: Cannot query field "unknown_field" on type "Dog". - locations: - - {line: 3, column: 9} -- name: Sub-field not defined - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment subFieldNotDefined on Human { - pets { - unknown_field - } - } - - errors: - - message: Cannot query field "unknown_field" on type "Pet". - locations: - - {line: 4, column: 11} -- name: Field not defined on inline fragment - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment fieldNotDefined on Pet { - ... on Dog { - meowVolume - } - } - - errors: - - message: Cannot query field "meowVolume" on type "Dog". Did you mean "barkVolume"? - locations: - - {line: 4, column: 11} -- name: Aliased field target not defined - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment aliasedFieldTargetNotDefined on Dog { - volume : mooVolume - } - - errors: - - message: Cannot query field "mooVolume" on type "Dog". Did you mean "barkVolume"? - locations: - - {line: 3, column: 9} -- name: Aliased lying field target not defined - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment aliasedLyingFieldTargetNotDefined on Dog { - barkVolume : kawVolume - } - - errors: - - message: Cannot query field "kawVolume" on type "Dog". Did you mean "barkVolume"? - locations: - - {line: 3, column: 9} -- name: Not defined on interface - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment notDefinedOnInterface on Pet { - tailLength - } - - errors: - - message: Cannot query field "tailLength" on type "Pet". - locations: - - {line: 3, column: 9} -- name: Defined on implementors but not on interface - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment definedOnImplementorsButNotInterface on Pet { - nickname - } - - errors: - - message: Cannot query field "nickname" on type "Pet". Did you mean to use an inline fragment on "Cat" or "Dog"? - locations: - - {line: 3, column: 9} -- name: Meta field selection on union - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment directFieldSelectionOnUnion on CatOrDog { - __typename - } - - errors: [] -- name: Direct field selection on union - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment directFieldSelectionOnUnion on CatOrDog { - directField - } - - errors: - - message: Cannot query field "directField" on type "CatOrDog". - locations: - - {line: 3, column: 9} -- name: Defined on implementors queried on union - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment definedOnImplementorsQueriedOnUnion on CatOrDog { - name - } - - errors: - - message: Cannot query field "name" on type "CatOrDog". Did you mean to use an inline fragment on "Pet", "Cat", or "Dog"? - locations: - - {line: 3, column: 9} -- name: valid field in inline fragment - rule: FieldsOnCorrectType - schema: 1 - query: |2- - - fragment objectFieldSelection on Pet { - ... on Dog { - name - } - ... { - name - } - } - - errors: [] diff --git a/internal/gqlparser/validator/imported/spec/FragmentsOnCompositeTypesRule.spec.yml b/internal/gqlparser/validator/imported/spec/FragmentsOnCompositeTypesRule.spec.yml deleted file mode 100644 index 3d9e7a89b7..0000000000 --- a/internal/gqlparser/validator/imported/spec/FragmentsOnCompositeTypesRule.spec.yml +++ /dev/null @@ -1,120 +0,0 @@ -- name: object is valid fragment type - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment validFragment on Dog { - barks - } - - errors: [] -- name: interface is valid fragment type - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment validFragment on Pet { - name - } - - errors: [] -- name: object is valid inline fragment type - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment validFragment on Pet { - ... on Dog { - barks - } - } - - errors: [] -- name: interface is valid inline fragment type - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment validFragment on Mammal { - ... on Canine { - name - } - } - - errors: [] -- name: inline fragment without type is valid - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment validFragment on Pet { - ... { - name - } - } - - errors: [] -- name: union is valid fragment type - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment validFragment on CatOrDog { - __typename - } - - errors: [] -- name: scalar is invalid fragment type - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment scalarFragment on Boolean { - bad - } - - errors: - - message: Fragment "scalarFragment" cannot condition on non composite type "Boolean". - locations: - - {line: 2, column: 34} -- name: enum is invalid fragment type - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment scalarFragment on FurColor { - bad - } - - errors: - - message: Fragment "scalarFragment" cannot condition on non composite type "FurColor". - locations: - - {line: 2, column: 34} -- name: input object is invalid fragment type - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment inputFragment on ComplexInput { - stringField - } - - errors: - - message: Fragment "inputFragment" cannot condition on non composite type "ComplexInput". - locations: - - {line: 2, column: 33} -- name: scalar is invalid inline fragment type - rule: FragmentsOnCompositeTypes - schema: 0 - query: |2- - - fragment invalidFragment on Pet { - ... on String { - barks - } - } - - errors: - - message: Fragment cannot condition on non composite type "String". - locations: - - {line: 3, column: 16} diff --git a/internal/gqlparser/validator/imported/spec/KnownArgumentNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/KnownArgumentNamesRule.spec.yml deleted file mode 100644 index 03ca5158a0..0000000000 --- a/internal/gqlparser/validator/imported/spec/KnownArgumentNamesRule.spec.yml +++ /dev/null @@ -1,195 +0,0 @@ -- name: single arg is known - rule: KnownArgumentNames - schema: 0 - query: |2- - - fragment argOnRequiredArg on Dog { - doesKnowCommand(dogCommand: SIT) - } - - errors: [] -- name: multiple args are known - rule: KnownArgumentNames - schema: 0 - query: |2- - - fragment multipleArgs on ComplicatedArgs { - multipleReqs(req1: 1, req2: 2) - } - - errors: [] -- name: ignores args of unknown fields - rule: KnownArgumentNames - schema: 0 - query: |2- - - fragment argOnUnknownField on Dog { - unknownField(unknownArg: SIT) - } - - errors: [] -- name: multiple args in reverse order are known - rule: KnownArgumentNames - schema: 0 - query: |2- - - fragment multipleArgsReverseOrder on ComplicatedArgs { - multipleReqs(req2: 2, req1: 1) - } - - errors: [] -- name: no args on optional arg - rule: KnownArgumentNames - schema: 0 - query: |2- - - fragment noArgOnOptionalArg on Dog { - isHouseTrained - } - - errors: [] -- name: args are known deeply - rule: KnownArgumentNames - schema: 0 - query: |2- - - { - dog { - doesKnowCommand(dogCommand: SIT) - } - human { - pet { - ... on Dog { - doesKnowCommand(dogCommand: SIT) - } - } - } - } - - errors: [] -- name: directive args are known - rule: KnownArgumentNames - schema: 0 - query: |2- - - { - dog @skip(if: true) - } - - errors: [] -- name: field args are invalid - rule: KnownArgumentNames - schema: 0 - query: |2- - - { - dog @skip(unless: true) - } - - errors: - - message: Unknown argument "unless" on directive "@skip". - locations: - - {line: 3, column: 19} -- name: directive without args is valid - rule: KnownArgumentNames - schema: 0 - query: |2- - - { - dog @onField - } - - errors: [] -- name: arg passed to directive without arg is reported - rule: KnownArgumentNames - schema: 0 - query: |2- - - { - dog @onField(if: true) - } - - errors: - - message: Unknown argument "if" on directive "@onField". - locations: - - {line: 3, column: 22} -- name: misspelled directive args are reported - rule: KnownArgumentNames - schema: 0 - query: |2- - - { - dog @skip(iff: true) - } - - errors: - - message: Unknown argument "iff" on directive "@skip". Did you mean "if"? - locations: - - {line: 3, column: 19} -- name: invalid arg name - rule: KnownArgumentNames - schema: 0 - query: |2- - - fragment invalidArgName on Dog { - doesKnowCommand(unknown: true) - } - - errors: - - message: Unknown argument "unknown" on field "Dog.doesKnowCommand". - locations: - - {line: 3, column: 25} -- name: misspelled arg name is reported - rule: KnownArgumentNames - schema: 0 - query: |2- - - fragment invalidArgName on Dog { - doesKnowCommand(DogCommand: true) - } - - errors: - - message: Unknown argument "DogCommand" on field "Dog.doesKnowCommand". Did you mean "dogCommand"? - locations: - - {line: 3, column: 25} -- name: unknown args amongst known args - rule: KnownArgumentNames - schema: 0 - query: |2- - - fragment oneGoodArgOneInvalidArg on Dog { - doesKnowCommand(whoKnows: 1, dogCommand: SIT, unknown: true) - } - - errors: - - message: Unknown argument "whoKnows" on field "Dog.doesKnowCommand". - locations: - - {line: 3, column: 25} - - message: Unknown argument "unknown" on field "Dog.doesKnowCommand". - locations: - - {line: 3, column: 55} -- name: unknown args deeply - rule: KnownArgumentNames - schema: 0 - query: |2- - - { - dog { - doesKnowCommand(unknown: true) - } - human { - pet { - ... on Dog { - doesKnowCommand(unknown: true) - } - } - } - } - - errors: - - message: Unknown argument "unknown" on field "Dog.doesKnowCommand". - locations: - - {line: 4, column: 27} - - message: Unknown argument "unknown" on field "Dog.doesKnowCommand". - locations: - - {line: 9, column: 31} diff --git a/internal/gqlparser/validator/imported/spec/KnownDirectivesRule.spec.yml b/internal/gqlparser/validator/imported/spec/KnownDirectivesRule.spec.yml deleted file mode 100644 index 36fc9a5bab..0000000000 --- a/internal/gqlparser/validator/imported/spec/KnownDirectivesRule.spec.yml +++ /dev/null @@ -1,162 +0,0 @@ -- name: with no directives - rule: KnownDirectives - schema: 2 - query: |2- - - query Foo { - name - ...Frag - } - - fragment Frag on Dog { - name - } - - errors: [] -- name: with standard directives - rule: KnownDirectives - schema: 2 - query: |2- - - { - human @skip(if: false) { - name - pets { - ... on Dog @include(if: true) { - name - } - } - } - } - - errors: [] -- name: with unknown directive - rule: KnownDirectives - schema: 2 - query: |2- - - { - human @unknown(directive: "value") { - name - } - } - - errors: - - message: Unknown directive "@unknown". - locations: - - {line: 3, column: 15} -- name: with many unknown directives - rule: KnownDirectives - schema: 2 - query: |2- - - { - __typename @unknown - human @unknown { - name - pets @unknown { - name - } - } - } - - errors: - - message: Unknown directive "@unknown". - locations: - - {line: 3, column: 20} - - message: Unknown directive "@unknown". - locations: - - {line: 4, column: 15} - - message: Unknown directive "@unknown". - locations: - - {line: 6, column: 16} -- name: with well placed directives - rule: KnownDirectives - schema: 2 - query: |2- - - query ($var: Boolean @onVariableDefinition) @onQuery { - human @onField { - ...Frag @onFragmentSpread - ... @onInlineFragment { - name @onField - } - } - } - - mutation @onMutation { - someField @onField - } - - subscription @onSubscription { - someField @onField - } - - fragment Frag on Human @onFragmentDefinition { - name @onField - } - - errors: [] -- name: with misplaced directives - rule: KnownDirectives - schema: 2 - query: |2- - - query ($var: Boolean @onQuery) @onMutation { - human @onQuery { - ...Frag @onQuery - ... @onQuery { - name @onQuery - } - } - } - - mutation @onQuery { - someField @onQuery - } - - subscription @onQuery { - someField @onQuery - } - - fragment Frag on Human @onQuery { - name @onQuery - } - - errors: - - message: Directive "@onQuery" may not be used on VARIABLE_DEFINITION. - locations: - - {line: 2, column: 28} - - message: Directive "@onMutation" may not be used on QUERY. - locations: - - {line: 2, column: 38} - - message: Directive "@onQuery" may not be used on FIELD. - locations: - - {line: 3, column: 15} - - message: Directive "@onQuery" may not be used on FRAGMENT_SPREAD. - locations: - - {line: 4, column: 19} - - message: Directive "@onQuery" may not be used on INLINE_FRAGMENT. - locations: - - {line: 5, column: 15} - - message: Directive "@onQuery" may not be used on FIELD. - locations: - - {line: 6, column: 18} - - message: Directive "@onQuery" may not be used on MUTATION. - locations: - - {line: 11, column: 16} - - message: Directive "@onQuery" may not be used on FIELD. - locations: - - {column: 19, line: 12} - - message: Directive "@onQuery" may not be used on SUBSCRIPTION. - locations: - - {column: 20, line: 15} - - message: Directive "@onQuery" may not be used on FIELD. - locations: - - {column: 19, line: 16} - - message: Directive "@onQuery" may not be used on FRAGMENT_DEFINITION. - locations: - - {column: 30, line: 19} - - message: Directive "@onQuery" may not be used on FIELD. - locations: - - {column: 14, line: 20} diff --git a/internal/gqlparser/validator/imported/spec/KnownFragmentNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/KnownFragmentNamesRule.spec.yml deleted file mode 100644 index ba997f49e8..0000000000 --- a/internal/gqlparser/validator/imported/spec/KnownFragmentNamesRule.spec.yml +++ /dev/null @@ -1,56 +0,0 @@ -- name: known fragment names are valid - rule: KnownFragmentNames - schema: 0 - query: |2- - - { - human(id: 4) { - ...HumanFields1 - ... on Human { - ...HumanFields2 - } - ... { - name - } - } - } - fragment HumanFields1 on Human { - name - ...HumanFields3 - } - fragment HumanFields2 on Human { - name - } - fragment HumanFields3 on Human { - name - } - - errors: [] -- name: unknown fragment names are invalid - rule: KnownFragmentNames - schema: 0 - query: |2- - - { - human(id: 4) { - ...UnknownFragment1 - ... on Human { - ...UnknownFragment2 - } - } - } - fragment HumanFields on Human { - name - ...UnknownFragment3 - } - - errors: - - message: Unknown fragment "UnknownFragment1". - locations: - - {line: 4, column: 14} - - message: Unknown fragment "UnknownFragment2". - locations: - - {line: 6, column: 16} - - message: Unknown fragment "UnknownFragment3". - locations: - - {line: 12, column: 12} diff --git a/internal/gqlparser/validator/imported/spec/KnownTypeNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/KnownTypeNamesRule.spec.yml deleted file mode 100644 index 2007840eca..0000000000 --- a/internal/gqlparser/validator/imported/spec/KnownTypeNamesRule.spec.yml +++ /dev/null @@ -1,64 +0,0 @@ -- name: known type names are valid - rule: KnownTypeNames - schema: 0 - query: |2- - - query Foo( - $var: String - $required: [Int!]! - $introspectionType: __EnumValue - ) { - user(id: 4) { - pets { ... on Pet { name }, ...PetFields, ... { name } } - } - } - - fragment PetFields on Pet { - name - } - - errors: [] -- name: unknown type names are invalid - rule: KnownTypeNames - schema: 0 - query: |2- - - query Foo($var: [JumbledUpLetters!]!) { - user(id: 4) { - name - pets { ... on Badger { name }, ...PetFields } - } - } - fragment PetFields on Peat { - name - } - - errors: - - message: Unknown type "JumbledUpLetters". - locations: - - {line: 2, column: 24} - - message: Unknown type "Badger". - locations: - - {line: 5, column: 25} - - message: Unknown type "Peat". Did you mean "Pet" or "Cat"? - locations: - - {line: 8, column: 29} -- name: references to standard scalars that are missing in schema - rule: KnownTypeNames - schema: 3 - query: |2- - - query ($id: ID, $float: Float, $int: Int) { - __typename - } - - errors: - - message: Unknown type "ID". - locations: - - {line: 2, column: 19} - - message: Unknown type "Float". - locations: - - {line: 2, column: 31} - - message: Unknown type "Int". - locations: - - {line: 2, column: 44} diff --git a/internal/gqlparser/validator/imported/spec/LoneAnonymousOperationRule.spec.yml b/internal/gqlparser/validator/imported/spec/LoneAnonymousOperationRule.spec.yml deleted file mode 100644 index 0950467b40..0000000000 --- a/internal/gqlparser/validator/imported/spec/LoneAnonymousOperationRule.spec.yml +++ /dev/null @@ -1,98 +0,0 @@ -- name: no operations - rule: LoneAnonymousOperation - schema: 0 - query: |2- - - fragment fragA on Type { - field - } - - errors: [] -- name: one anon operation - rule: LoneAnonymousOperation - schema: 0 - query: |2- - - { - field - } - - errors: [] -- name: multiple named operations - rule: LoneAnonymousOperation - schema: 0 - query: |2- - - query Foo { - field - } - - query Bar { - field - } - - errors: [] -- name: anon operation with fragment - rule: LoneAnonymousOperation - schema: 0 - query: |2- - - { - ...Foo - } - fragment Foo on Type { - field - } - - errors: [] -- name: multiple anon operations - rule: LoneAnonymousOperation - schema: 0 - query: |2- - - { - fieldA - } - { - fieldB - } - - errors: - - message: This anonymous operation must be the only defined operation. - locations: - - {line: 2, column: 7} - - message: This anonymous operation must be the only defined operation. - locations: - - {line: 5, column: 7} -- name: anon operation with a mutation - rule: LoneAnonymousOperation - schema: 0 - query: |2- - - { - fieldA - } - mutation Foo { - fieldB - } - - errors: - - message: This anonymous operation must be the only defined operation. - locations: - - {line: 2, column: 7} -- name: anon operation with a subscription - rule: LoneAnonymousOperation - schema: 0 - query: |2- - - { - fieldA - } - subscription Foo { - fieldB - } - - errors: - - message: This anonymous operation must be the only defined operation. - locations: - - {line: 2, column: 7} diff --git a/internal/gqlparser/validator/imported/spec/LoneSchemaDefinitionRule.spec.yml b/internal/gqlparser/validator/imported/spec/LoneSchemaDefinitionRule.spec.yml deleted file mode 100644 index fe51488c70..0000000000 --- a/internal/gqlparser/validator/imported/spec/LoneSchemaDefinitionRule.spec.yml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/internal/gqlparser/validator/imported/spec/NoDeprecatedCustomRule.spec.yml b/internal/gqlparser/validator/imported/spec/NoDeprecatedCustomRule.spec.yml deleted file mode 100644 index ff8df4ac56..0000000000 --- a/internal/gqlparser/validator/imported/spec/NoDeprecatedCustomRule.spec.yml +++ /dev/null @@ -1,209 +0,0 @@ -- name: no deprecated fields/ignores fields that are not deprecated - rule: NoDeprecatedCustom - schema: 4 - query: |2- - - { - normalField - } - - errors: [] -- name: no deprecated fields/ignores unknown fields - rule: NoDeprecatedCustom - schema: 4 - query: |2- - - { - unknownField - } - - fragment UnknownFragment on UnknownType { - deprecatedField - } - - errors: [] -- name: no deprecated fields/reports error when a deprecated field is selected - rule: NoDeprecatedCustom - schema: 4 - query: |2- - - { - deprecatedField - } - - fragment QueryFragment on Query { - deprecatedField - } - - errors: - - message: The field Query.deprecatedField is deprecated. Some field reason. - locations: - - {line: 3, column: 11} - - message: The field Query.deprecatedField is deprecated. Some field reason. - locations: - - {line: 7, column: 11} -- name: no deprecated arguments on fields/ignores arguments that are not deprecated - rule: NoDeprecatedCustom - schema: 5 - query: |2- - - { - normalField(normalArg: "") - } - - errors: [] -- name: no deprecated arguments on fields/ignores unknown arguments - rule: NoDeprecatedCustom - schema: 5 - query: |2- - - { - someField(unknownArg: "") - unknownField(deprecatedArg: "") - } - - errors: [] -- name: no deprecated arguments on fields/reports error when a deprecated argument is used - rule: NoDeprecatedCustom - schema: 5 - query: |2- - - { - someField(deprecatedArg: "") - } - - errors: - - message: Field "Query.someField" argument "deprecatedArg" is deprecated. Some arg reason. - locations: - - {line: 3, column: 21} -- name: no deprecated arguments on directives/ignores arguments that are not deprecated - rule: NoDeprecatedCustom - schema: 6 - query: |2- - - { - someField @someDirective(normalArg: "") - } - - errors: [] -- name: no deprecated arguments on directives/ignores unknown arguments - rule: NoDeprecatedCustom - schema: 6 - query: |2- - - { - someField @someDirective(unknownArg: "") - someField @unknownDirective(deprecatedArg: "") - } - - errors: [] -- name: no deprecated arguments on directives/reports error when a deprecated argument is used - rule: NoDeprecatedCustom - schema: 6 - query: |2- - - { - someField @someDirective(deprecatedArg: "") - } - - errors: - - message: Directive "@someDirective" argument "deprecatedArg" is deprecated. Some arg reason. - locations: - - {line: 3, column: 36} -- name: no deprecated input fields/ignores input fields that are not deprecated - rule: NoDeprecatedCustom - schema: 7 - query: |2- - - { - someField( - someArg: { normalField: "" } - ) @someDirective(someArg: { normalField: "" }) - } - - errors: [] -- name: no deprecated input fields/ignores unknown input fields - rule: NoDeprecatedCustom - schema: 7 - query: |2- - - { - someField( - someArg: { unknownField: "" } - ) - - someField( - unknownArg: { unknownField: "" } - ) - - unknownField( - unknownArg: { unknownField: "" } - ) - } - - errors: [] -- name: no deprecated input fields/reports error when a deprecated input field is used - rule: NoDeprecatedCustom - schema: 7 - query: |2- - - { - someField( - someArg: { deprecatedField: "" } - ) @someDirective(someArg: { deprecatedField: "" }) - } - - errors: - - message: The input field InputType.deprecatedField is deprecated. Some input field reason. - locations: - - {line: 4, column: 24} - - message: The input field InputType.deprecatedField is deprecated. Some input field reason. - locations: - - {line: 5, column: 39} -- name: no deprecated enum values/ignores enum values that are not deprecated - rule: NoDeprecatedCustom - schema: 8 - query: |2- - - { - normalField(enumArg: NORMAL_VALUE) - } - - errors: [] -- name: no deprecated enum values/ignores unknown enum values - rule: NoDeprecatedCustom - schema: 8 - query: |2- - - query ( - $unknownValue: EnumType = UNKNOWN_VALUE - $unknownType: UnknownType = UNKNOWN_VALUE - ) { - someField(enumArg: UNKNOWN_VALUE) - someField(unknownArg: UNKNOWN_VALUE) - unknownField(unknownArg: UNKNOWN_VALUE) - } - - fragment SomeFragment on Query { - someField(enumArg: UNKNOWN_VALUE) - } - - errors: [] -- name: no deprecated enum values/reports error when a deprecated enum value is used - rule: NoDeprecatedCustom - schema: 8 - query: |2- - - query ( - $variable: EnumType = DEPRECATED_VALUE - ) { - someField(enumArg: DEPRECATED_VALUE) - } - - errors: - - message: The enum value "EnumType.DEPRECATED_VALUE" is deprecated. Some enum reason. - locations: - - {line: 3, column: 33} - - message: The enum value "EnumType.DEPRECATED_VALUE" is deprecated. Some enum reason. - locations: - - {line: 5, column: 30} diff --git a/internal/gqlparser/validator/imported/spec/NoFragmentCyclesRule.spec.yml b/internal/gqlparser/validator/imported/spec/NoFragmentCyclesRule.spec.yml deleted file mode 100644 index b2bbdb8abd..0000000000 --- a/internal/gqlparser/validator/imported/spec/NoFragmentCyclesRule.spec.yml +++ /dev/null @@ -1,225 +0,0 @@ -- name: single reference is valid - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Dog { ...fragB } - fragment fragB on Dog { name } - - errors: [] -- name: spreading twice is not circular - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Dog { ...fragB, ...fragB } - fragment fragB on Dog { name } - - errors: [] -- name: spreading twice indirectly is not circular - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Dog { ...fragB, ...fragC } - fragment fragB on Dog { ...fragC } - fragment fragC on Dog { name } - - errors: [] -- name: double spread within abstract types - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment nameFragment on Pet { - ... on Dog { name } - ... on Cat { name } - } - - fragment spreadsInAnon on Pet { - ... on Dog { ...nameFragment } - ... on Cat { ...nameFragment } - } - - errors: [] -- name: does not false positive on unknown fragment - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment nameFragment on Pet { - ...UnknownFragment - } - - errors: [] -- name: spreading recursively within field fails - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Human { relatives { ...fragA } }, - - errors: - - message: Cannot spread fragment "fragA" within itself. - locations: - - {line: 2, column: 45} -- name: no spreading itself directly - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Dog { ...fragA } - - errors: - - message: Cannot spread fragment "fragA" within itself. - locations: - - {line: 2, column: 31} -- name: no spreading itself directly within inline fragment - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Pet { - ... on Dog { - ...fragA - } - } - - errors: - - message: Cannot spread fragment "fragA" within itself. - locations: - - {line: 4, column: 11} -- name: no spreading itself indirectly - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Dog { ...fragB } - fragment fragB on Dog { ...fragA } - - errors: - - message: Cannot spread fragment "fragA" within itself via "fragB". - locations: - - {line: 2, column: 31} - - {line: 3, column: 31} -- name: no spreading itself indirectly reports opposite order - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragB on Dog { ...fragA } - fragment fragA on Dog { ...fragB } - - errors: - - message: Cannot spread fragment "fragB" within itself via "fragA". - locations: - - {line: 2, column: 31} - - {line: 3, column: 31} -- name: no spreading itself indirectly within inline fragment - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Pet { - ... on Dog { - ...fragB - } - } - fragment fragB on Pet { - ... on Dog { - ...fragA - } - } - - errors: - - message: Cannot spread fragment "fragA" within itself via "fragB". - locations: - - {line: 4, column: 11} - - {line: 9, column: 11} -- name: no spreading itself deeply - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Dog { ...fragB } - fragment fragB on Dog { ...fragC } - fragment fragC on Dog { ...fragO } - fragment fragX on Dog { ...fragY } - fragment fragY on Dog { ...fragZ } - fragment fragZ on Dog { ...fragO } - fragment fragO on Dog { ...fragP } - fragment fragP on Dog { ...fragA, ...fragX } - - errors: - - message: Cannot spread fragment "fragA" within itself via "fragB", "fragC", "fragO", "fragP". - locations: - - {line: 2, column: 31} - - {line: 3, column: 31} - - {line: 4, column: 31} - - {line: 8, column: 31} - - {line: 9, column: 31} - - message: Cannot spread fragment "fragO" within itself via "fragP", "fragX", "fragY", "fragZ". - locations: - - {line: 8, column: 31} - - {line: 9, column: 41} - - {line: 5, column: 31} - - {line: 6, column: 31} - - {line: 7, column: 31} -- name: no spreading itself deeply two paths - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Dog { ...fragB, ...fragC } - fragment fragB on Dog { ...fragA } - fragment fragC on Dog { ...fragA } - - errors: - - message: Cannot spread fragment "fragA" within itself via "fragB". - locations: - - {line: 2, column: 31} - - {line: 3, column: 31} - - message: Cannot spread fragment "fragA" within itself via "fragC". - locations: - - {line: 2, column: 41} - - {line: 4, column: 31} -- name: no spreading itself deeply two paths -- alt traverse order - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Dog { ...fragC } - fragment fragB on Dog { ...fragC } - fragment fragC on Dog { ...fragA, ...fragB } - - errors: - - message: Cannot spread fragment "fragA" within itself via "fragC". - locations: - - {line: 2, column: 31} - - {line: 4, column: 31} - - message: Cannot spread fragment "fragC" within itself via "fragB". - locations: - - {line: 4, column: 41} - - {line: 3, column: 31} -- name: no spreading itself deeply and immediately - rule: NoFragmentCycles - schema: 0 - query: |2- - - fragment fragA on Dog { ...fragB } - fragment fragB on Dog { ...fragB, ...fragC } - fragment fragC on Dog { ...fragA, ...fragB } - - errors: - - message: Cannot spread fragment "fragB" within itself. - locations: - - {line: 3, column: 31} - - message: Cannot spread fragment "fragA" within itself via "fragB", "fragC". - locations: - - {line: 2, column: 31} - - {line: 3, column: 41} - - {line: 4, column: 31} - - message: Cannot spread fragment "fragB" within itself via "fragC". - locations: - - {line: 3, column: 41} - - {line: 4, column: 41} diff --git a/internal/gqlparser/validator/imported/spec/NoSchemaIntrospectionCustomRule.spec.yml b/internal/gqlparser/validator/imported/spec/NoSchemaIntrospectionCustomRule.spec.yml deleted file mode 100644 index 12ca2f27d0..0000000000 --- a/internal/gqlparser/validator/imported/spec/NoSchemaIntrospectionCustomRule.spec.yml +++ /dev/null @@ -1,102 +0,0 @@ -- name: ignores valid fields including __typename - rule: NoSchemaIntrospectionCustom - schema: 9 - query: |2- - - { - someQuery { - __typename - someField - } - } - - errors: [] -- name: ignores fields not in the schema - rule: NoSchemaIntrospectionCustom - schema: 9 - query: |2- - - { - __introspect - } - - errors: [] -- name: reports error when a field with an introspection type is requested - rule: NoSchemaIntrospectionCustom - schema: 9 - query: |2- - - { - __schema { - queryType { - name - } - } - } - - errors: - - message: GraphQL introspection has been disabled, but the requested query contained the field "__schema". - locations: - - {line: 3, column: 9} - - message: GraphQL introspection has been disabled, but the requested query contained the field "queryType". - locations: - - {line: 4, column: 11} -- name: reports error when a field with an introspection type is requested and aliased - rule: NoSchemaIntrospectionCustom - schema: 9 - query: |2- - - { - s: __schema { - queryType { - name - } - } - } - - errors: - - message: GraphQL introspection has been disabled, but the requested query contained the field "__schema". - locations: - - {line: 3, column: 9} - - message: GraphQL introspection has been disabled, but the requested query contained the field "queryType". - locations: - - {line: 4, column: 11} -- name: reports error when using a fragment with a field with an introspection type - rule: NoSchemaIntrospectionCustom - schema: 9 - query: |2- - - { - ...QueryFragment - } - - fragment QueryFragment on Query { - __schema { - queryType { - name - } - } - } - - errors: - - message: GraphQL introspection has been disabled, but the requested query contained the field "__schema". - locations: - - {line: 7, column: 9} - - message: GraphQL introspection has been disabled, but the requested query contained the field "queryType". - locations: - - {line: 8, column: 11} -- name: reports error for non-standard introspection fields - rule: NoSchemaIntrospectionCustom - schema: 9 - query: |2- - - { - someQuery { - introspectionField - } - } - - errors: - - message: GraphQL introspection has been disabled, but the requested query contained the field "introspectionField". - locations: - - {line: 4, column: 11} diff --git a/internal/gqlparser/validator/imported/spec/NoUndefinedVariablesRule.spec.yml b/internal/gqlparser/validator/imported/spec/NoUndefinedVariablesRule.spec.yml deleted file mode 100644 index 84e4c2f79d..0000000000 --- a/internal/gqlparser/validator/imported/spec/NoUndefinedVariablesRule.spec.yml +++ /dev/null @@ -1,356 +0,0 @@ -- name: all variables defined - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - field(a: $a, b: $b, c: $c) - } - - errors: [] -- name: all variables deeply defined - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - field(a: $a) { - field(b: $b) { - field(c: $c) - } - } - } - - errors: [] -- name: all variables deeply in inline fragments defined - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - ... on Type { - field(a: $a) { - field(b: $b) { - ... on Type { - field(c: $c) - } - } - } - } - } - - errors: [] -- name: all variables in fragments deeply defined - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - ...FragA - } - fragment FragA on Type { - field(a: $a) { - ...FragB - } - } - fragment FragB on Type { - field(b: $b) { - ...FragC - } - } - fragment FragC on Type { - field(c: $c) - } - - errors: [] -- name: variable within single fragment defined in multiple operations - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String) { - ...FragA - } - query Bar($a: String) { - ...FragA - } - fragment FragA on Type { - field(a: $a) - } - - errors: [] -- name: variable within fragments defined in operations - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String) { - ...FragA - } - query Bar($b: String) { - ...FragB - } - fragment FragA on Type { - field(a: $a) - } - fragment FragB on Type { - field(b: $b) - } - - errors: [] -- name: variable within recursive fragment defined - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String) { - ...FragA - } - fragment FragA on Type { - field(a: $a) { - ...FragA - } - } - - errors: [] -- name: variable not defined - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - field(a: $a, b: $b, c: $c, d: $d) - } - - errors: - - message: Variable "$d" is not defined by operation "Foo". - locations: - - {line: 3, column: 39} - - {line: 2, column: 7} -- name: variable not defined by un-named query - rule: NoUndefinedVariables - schema: 0 - query: |2- - - { - field(a: $a) - } - - errors: - - message: Variable "$a" is not defined. - locations: - - {line: 3, column: 18} - - {line: 2, column: 7} -- name: multiple variables not defined - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($b: String) { - field(a: $a, b: $b, c: $c) - } - - errors: - - message: Variable "$a" is not defined by operation "Foo". - locations: - - {line: 3, column: 18} - - {line: 2, column: 7} - - message: Variable "$c" is not defined by operation "Foo". - locations: - - {line: 3, column: 32} - - {line: 2, column: 7} -- name: variable in fragment not defined by un-named query - rule: NoUndefinedVariables - schema: 0 - query: |2- - - { - ...FragA - } - fragment FragA on Type { - field(a: $a) - } - - errors: - - message: Variable "$a" is not defined. - locations: - - {line: 6, column: 18} - - {line: 2, column: 7} -- name: variable in fragment not defined by operation - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String) { - ...FragA - } - fragment FragA on Type { - field(a: $a) { - ...FragB - } - } - fragment FragB on Type { - field(b: $b) { - ...FragC - } - } - fragment FragC on Type { - field(c: $c) - } - - errors: - - message: Variable "$c" is not defined by operation "Foo". - locations: - - {line: 16, column: 18} - - {line: 2, column: 7} -- name: multiple variables in fragments not defined - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($b: String) { - ...FragA - } - fragment FragA on Type { - field(a: $a) { - ...FragB - } - } - fragment FragB on Type { - field(b: $b) { - ...FragC - } - } - fragment FragC on Type { - field(c: $c) - } - - errors: - - message: Variable "$a" is not defined by operation "Foo". - locations: - - {line: 6, column: 18} - - {line: 2, column: 7} - - message: Variable "$c" is not defined by operation "Foo". - locations: - - {line: 16, column: 18} - - {line: 2, column: 7} -- name: single variable in fragment not defined by multiple operations - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($a: String) { - ...FragAB - } - query Bar($a: String) { - ...FragAB - } - fragment FragAB on Type { - field(a: $a, b: $b) - } - - errors: - - message: Variable "$b" is not defined by operation "Foo". - locations: - - {line: 9, column: 25} - - {line: 2, column: 7} - - message: Variable "$b" is not defined by operation "Bar". - locations: - - {line: 9, column: 25} - - {line: 5, column: 7} -- name: variables in fragment not defined by multiple operations - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($b: String) { - ...FragAB - } - query Bar($a: String) { - ...FragAB - } - fragment FragAB on Type { - field(a: $a, b: $b) - } - - errors: - - message: Variable "$a" is not defined by operation "Foo". - locations: - - {line: 9, column: 18} - - {line: 2, column: 7} - - message: Variable "$b" is not defined by operation "Bar". - locations: - - {line: 9, column: 25} - - {line: 5, column: 7} -- name: variable in fragment used by other operation - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($b: String) { - ...FragA - } - query Bar($a: String) { - ...FragB - } - fragment FragA on Type { - field(a: $a) - } - fragment FragB on Type { - field(b: $b) - } - - errors: - - message: Variable "$a" is not defined by operation "Foo". - locations: - - {line: 9, column: 18} - - {line: 2, column: 7} - - message: Variable "$b" is not defined by operation "Bar". - locations: - - {line: 12, column: 18} - - {line: 5, column: 7} -- name: multiple undefined variables produce multiple errors - rule: NoUndefinedVariables - schema: 0 - query: |2- - - query Foo($b: String) { - ...FragAB - } - query Bar($a: String) { - ...FragAB - } - fragment FragAB on Type { - field1(a: $a, b: $b) - ...FragC - field3(a: $a, b: $b) - } - fragment FragC on Type { - field2(c: $c) - } - - errors: - - message: Variable "$a" is not defined by operation "Foo". - locations: - - {line: 9, column: 19} - - {line: 2, column: 7} - - message: Variable "$a" is not defined by operation "Foo". - locations: - - {line: 11, column: 19} - - {line: 2, column: 7} - - message: Variable "$c" is not defined by operation "Foo". - locations: - - {line: 14, column: 19} - - {line: 2, column: 7} - - message: Variable "$b" is not defined by operation "Bar". - locations: - - {line: 9, column: 26} - - {line: 5, column: 7} - - message: Variable "$b" is not defined by operation "Bar". - locations: - - {line: 11, column: 26} - - {line: 5, column: 7} - - message: Variable "$c" is not defined by operation "Bar". - locations: - - {line: 14, column: 19} - - {line: 5, column: 7} diff --git a/internal/gqlparser/validator/imported/spec/NoUnusedFragmentsRule.spec.yml b/internal/gqlparser/validator/imported/spec/NoUnusedFragmentsRule.spec.yml deleted file mode 100644 index 42f14df085..0000000000 --- a/internal/gqlparser/validator/imported/spec/NoUnusedFragmentsRule.spec.yml +++ /dev/null @@ -1,150 +0,0 @@ -- name: all fragment names are used - rule: NoUnusedFragments - schema: 0 - query: |2- - - { - human(id: 4) { - ...HumanFields1 - ... on Human { - ...HumanFields2 - } - } - } - fragment HumanFields1 on Human { - name - ...HumanFields3 - } - fragment HumanFields2 on Human { - name - } - fragment HumanFields3 on Human { - name - } - - errors: [] -- name: all fragment names are used by multiple operations - rule: NoUnusedFragments - schema: 0 - query: |2- - - query Foo { - human(id: 4) { - ...HumanFields1 - } - } - query Bar { - human(id: 4) { - ...HumanFields2 - } - } - fragment HumanFields1 on Human { - name - ...HumanFields3 - } - fragment HumanFields2 on Human { - name - } - fragment HumanFields3 on Human { - name - } - - errors: [] -- name: contains unknown fragments - rule: NoUnusedFragments - schema: 0 - query: |2- - - query Foo { - human(id: 4) { - ...HumanFields1 - } - } - query Bar { - human(id: 4) { - ...HumanFields2 - } - } - fragment HumanFields1 on Human { - name - ...HumanFields3 - } - fragment HumanFields2 on Human { - name - } - fragment HumanFields3 on Human { - name - } - fragment Unused1 on Human { - name - } - fragment Unused2 on Human { - name - } - - errors: - - message: Fragment "Unused1" is never used. - locations: - - {line: 22, column: 7} - - message: Fragment "Unused2" is never used. - locations: - - {line: 25, column: 7} -- name: contains unknown fragments with ref cycle - rule: NoUnusedFragments - schema: 0 - query: |2- - - query Foo { - human(id: 4) { - ...HumanFields1 - } - } - query Bar { - human(id: 4) { - ...HumanFields2 - } - } - fragment HumanFields1 on Human { - name - ...HumanFields3 - } - fragment HumanFields2 on Human { - name - } - fragment HumanFields3 on Human { - name - } - fragment Unused1 on Human { - name - ...Unused2 - } - fragment Unused2 on Human { - name - ...Unused1 - } - - errors: - - message: Fragment "Unused1" is never used. - locations: - - {line: 22, column: 7} - - message: Fragment "Unused2" is never used. - locations: - - {line: 26, column: 7} -- name: contains unknown and undef fragments - rule: NoUnusedFragments - schema: 0 - query: |2- - - query Foo { - human(id: 4) { - ...bar - } - } - fragment foo on Human { - name - } - - errors: - - message: Fragment "foo" is never used. - locations: - - {line: 7, column: 7} diff --git a/internal/gqlparser/validator/imported/spec/NoUnusedVariablesRule.spec.yml b/internal/gqlparser/validator/imported/spec/NoUnusedVariablesRule.spec.yml deleted file mode 100644 index 515a9ffaeb..0000000000 --- a/internal/gqlparser/validator/imported/spec/NoUnusedVariablesRule.spec.yml +++ /dev/null @@ -1,227 +0,0 @@ -- name: uses all variables - rule: NoUnusedVariables - schema: 0 - query: |2- - - query ($a: String, $b: String, $c: String) { - field(a: $a, b: $b, c: $c) - } - - errors: [] -- name: uses all variables deeply - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - field(a: $a) { - field(b: $b) { - field(c: $c) - } - } - } - - errors: [] -- name: uses all variables deeply in inline fragments - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - ... on Type { - field(a: $a) { - field(b: $b) { - ... on Type { - field(c: $c) - } - } - } - } - } - - errors: [] -- name: uses all variables in fragments - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - ...FragA - } - fragment FragA on Type { - field(a: $a) { - ...FragB - } - } - fragment FragB on Type { - field(b: $b) { - ...FragC - } - } - fragment FragC on Type { - field(c: $c) - } - - errors: [] -- name: variable used by fragment in multiple operations - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($a: String) { - ...FragA - } - query Bar($b: String) { - ...FragB - } - fragment FragA on Type { - field(a: $a) - } - fragment FragB on Type { - field(b: $b) - } - - errors: [] -- name: variable used by recursive fragment - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($a: String) { - ...FragA - } - fragment FragA on Type { - field(a: $a) { - ...FragA - } - } - - errors: [] -- name: variable not used - rule: NoUnusedVariables - schema: 0 - query: |2- - - query ($a: String, $b: String, $c: String) { - field(a: $a, b: $b) - } - - errors: - - message: Variable "$c" is never used. - locations: - - {line: 2, column: 38} -- name: multiple variables not used - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - field(b: $b) - } - - errors: - - message: Variable "$a" is never used in operation "Foo". - locations: - - {line: 2, column: 17} - - message: Variable "$c" is never used in operation "Foo". - locations: - - {line: 2, column: 41} -- name: variable not used in fragments - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - ...FragA - } - fragment FragA on Type { - field(a: $a) { - ...FragB - } - } - fragment FragB on Type { - field(b: $b) { - ...FragC - } - } - fragment FragC on Type { - field - } - - errors: - - message: Variable "$c" is never used in operation "Foo". - locations: - - {line: 2, column: 41} -- name: multiple variables not used in fragments - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($a: String, $b: String, $c: String) { - ...FragA - } - fragment FragA on Type { - field { - ...FragB - } - } - fragment FragB on Type { - field(b: $b) { - ...FragC - } - } - fragment FragC on Type { - field - } - - errors: - - message: Variable "$a" is never used in operation "Foo". - locations: - - {line: 2, column: 17} - - message: Variable "$c" is never used in operation "Foo". - locations: - - {line: 2, column: 41} -- name: variable not used by unreferenced fragment - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($b: String) { - ...FragA - } - fragment FragA on Type { - field(a: $a) - } - fragment FragB on Type { - field(b: $b) - } - - errors: - - message: Variable "$b" is never used in operation "Foo". - locations: - - {line: 2, column: 17} -- name: variable not used by fragment used by other operation - rule: NoUnusedVariables - schema: 0 - query: |2- - - query Foo($b: String) { - ...FragA - } - query Bar($a: String) { - ...FragB - } - fragment FragA on Type { - field(a: $a) - } - fragment FragB on Type { - field(b: $b) - } - - errors: - - message: Variable "$b" is never used in operation "Foo". - locations: - - {line: 2, column: 17} - - message: Variable "$a" is never used in operation "Bar". - locations: - - {line: 5, column: 17} diff --git a/internal/gqlparser/validator/imported/spec/OverlappingFieldsCanBeMergedRule.spec.yml b/internal/gqlparser/validator/imported/spec/OverlappingFieldsCanBeMergedRule.spec.yml deleted file mode 100644 index 895e40201b..0000000000 --- a/internal/gqlparser/validator/imported/spec/OverlappingFieldsCanBeMergedRule.spec.yml +++ /dev/null @@ -1,909 +0,0 @@ -- name: unique fields - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment uniqueFields on Dog { - name - nickname - } - - errors: [] -- name: identical fields - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment mergeIdenticalFields on Dog { - name - name - } - - errors: [] -- name: identical fields with identical args - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment mergeIdenticalFieldsWithIdenticalArgs on Dog { - doesKnowCommand(dogCommand: SIT) - doesKnowCommand(dogCommand: SIT) - } - - errors: [] -- name: identical fields with identical directives - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment mergeSameFieldsWithSameDirectives on Dog { - name @include(if: true) - name @include(if: true) - } - - errors: [] -- name: different args with different aliases - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment differentArgsWithDifferentAliases on Dog { - knowsSit: doesKnowCommand(dogCommand: SIT) - knowsDown: doesKnowCommand(dogCommand: DOWN) - } - - errors: [] -- name: different directives with different aliases - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment differentDirectivesWithDifferentAliases on Dog { - nameIfTrue: name @include(if: true) - nameIfFalse: name @include(if: false) - } - - errors: [] -- name: different skip/include directives accepted - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment differentDirectivesWithDifferentAliases on Dog { - name @include(if: true) - name @include(if: false) - } - - errors: [] -- name: Same aliases with different field targets - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment sameAliasesWithDifferentFieldTargets on Dog { - fido: name - fido: nickname - } - - errors: - - message: Fields "fido" conflict because "name" and "nickname" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 4, column: 9} -- name: Same aliases allowed on non-overlapping fields - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment sameAliasesWithDifferentFieldTargets on Pet { - ... on Dog { - name - } - ... on Cat { - name: nickname - } - } - - errors: [] -- name: Alias masking direct field access - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment aliasMaskingDirectFieldAccess on Dog { - name: nickname - name - } - - errors: - - message: Fields "name" conflict because "nickname" and "name" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 4, column: 9} -- name: different args, second adds an argument - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment conflictingArgs on Dog { - doesKnowCommand - doesKnowCommand(dogCommand: HEEL) - } - - errors: - - message: Fields "doesKnowCommand" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 4, column: 9} -- name: different args, second missing an argument - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment conflictingArgs on Dog { - doesKnowCommand(dogCommand: SIT) - doesKnowCommand - } - - errors: - - message: Fields "doesKnowCommand" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 4, column: 9} -- name: conflicting arg values - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment conflictingArgs on Dog { - doesKnowCommand(dogCommand: SIT) - doesKnowCommand(dogCommand: HEEL) - } - - errors: - - message: Fields "doesKnowCommand" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 4, column: 9} -- name: conflicting arg names - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment conflictingArgs on Dog { - isAtLocation(x: 0) - isAtLocation(y: 0) - } - - errors: - - message: Fields "isAtLocation" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 4, column: 9} -- name: allows different args where no conflict is possible - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment conflictingArgs on Pet { - ... on Dog { - name(surname: true) - } - ... on Cat { - name - } - } - - errors: [] -- name: allows different order of args - rule: OverlappingFieldsCanBeMerged - schema: 10 - query: |2- - - { - someField(a: null, b: null) - someField(b: null, a: null) - } - - errors: [] -- name: allows different order of input object fields in arg values - rule: OverlappingFieldsCanBeMerged - schema: 11 - query: |2- - - { - someField(arg: { a: null, b: null }) - someField(arg: { b: null, a: null }) - } - - errors: [] -- name: encounters conflict in fragments - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - ...A - ...B - } - fragment A on Type { - x: a - } - fragment B on Type { - x: b - } - - errors: - - message: Fields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 7, column: 9} - - {line: 10, column: 9} -- name: reports each conflict once - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - f1 { - ...A - ...B - } - f2 { - ...B - ...A - } - f3 { - ...A - ...B - x: c - } - } - fragment A on Type { - x: a - } - fragment B on Type { - x: b - } - - errors: - - message: Fields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 18, column: 9} - - {line: 21, column: 9} - - message: Fields "x" conflict because "c" and "a" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 14, column: 11} - - {line: 18, column: 9} - - message: Fields "x" conflict because "c" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 14, column: 11} - - {line: 21, column: 9} -- name: deep conflict - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - field { - x: a - }, - field { - x: b - } - } - - errors: - - message: Fields "field" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 4, column: 11} - - {line: 6, column: 9} - - {line: 7, column: 11} -- name: deep conflict with multiple issues - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - field { - x: a - y: c - }, - field { - x: b - y: d - } - } - - errors: - - message: Fields "field" conflict because subfields "x" conflict because "a" and "b" are different fields and subfields "y" conflict because "c" and "d" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 4, column: 11} - - {line: 5, column: 11} - - {line: 7, column: 9} - - {line: 8, column: 11} - - {line: 9, column: 11} -- name: very deep conflict - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - field { - deepField { - x: a - } - }, - field { - deepField { - x: b - } - } - } - - errors: - - message: Fields "field" conflict because subfields "deepField" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 4, column: 11} - - {line: 5, column: 13} - - {line: 8, column: 9} - - {line: 9, column: 11} - - {line: 10, column: 13} -- name: reports deep conflict to nearest common ancestor - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - field { - deepField { - x: a - } - deepField { - x: b - } - }, - field { - deepField { - y - } - } - } - - errors: - - message: Fields "deepField" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 4, column: 11} - - {line: 5, column: 13} - - {line: 7, column: 11} - - {line: 8, column: 13} -- name: reports deep conflict to nearest common ancestor in fragments - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - field { - ...F - } - field { - ...F - } - } - fragment F on T { - deepField { - deeperField { - x: a - } - deeperField { - x: b - } - }, - deepField { - deeperField { - y - } - } - } - - errors: - - message: Fields "deeperField" conflict because subfields "x" conflict because "a" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 12, column: 11} - - {line: 13, column: 13} - - {line: 15, column: 11} - - {line: 16, column: 13} -- name: reports deep conflict in nested fragments - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - field { - ...F - } - field { - ...I - } - } - fragment F on T { - x: a - ...G - } - fragment G on T { - y: c - } - fragment I on T { - y: d - ...J - } - fragment J on T { - x: b - } - - errors: - - message: Fields "field" conflict because subfields "x" conflict because "a" and "b" are different fields and subfields "y" conflict because "c" and "d" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 11, column: 9} - - {line: 15, column: 9} - - {line: 6, column: 9} - - {line: 22, column: 9} - - {line: 18, column: 9} -- name: ignores unknown fragments - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - field - ...Unknown - ...Known - } - - fragment Known on T { - field - ...OtherUnknown - } - - errors: [] -- name: return types must be unambiguous/conflicting return types which potentially overlap - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ...on IntBox { - scalar - } - ...on NonNullStringBox1 { - scalar - } - } - } - - errors: - - message: Fields "scalar" conflict because they return conflicting types "Int" and "String!". Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 5, column: 17} - - {line: 8, column: 17} -- name: return types must be unambiguous/compatible return shapes on different return types - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ... on SomeBox { - deepBox { - unrelatedField - } - } - ... on StringBox { - deepBox { - unrelatedField - } - } - } - } - - errors: [] -- name: return types must be unambiguous/disallows differing return types despite no overlap - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ... on IntBox { - scalar - } - ... on StringBox { - scalar - } - } - } - - errors: - - message: Fields "scalar" conflict because they return conflicting types "Int" and "String". Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 5, column: 17} - - {line: 8, column: 17} -- name: return types must be unambiguous/reports correctly when a non-exclusive follows an exclusive - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ... on IntBox { - deepBox { - ...X - } - } - } - someBox { - ... on StringBox { - deepBox { - ...Y - } - } - } - memoed: someBox { - ... on IntBox { - deepBox { - ...X - } - } - } - memoed: someBox { - ... on StringBox { - deepBox { - ...Y - } - } - } - other: someBox { - ...X - } - other: someBox { - ...Y - } - } - fragment X on SomeBox { - scalar - } - fragment Y on SomeBox { - scalar: unrelatedField - } - - errors: - - message: Fields "other" conflict because subfields "scalar" conflict because "scalar" and "unrelatedField" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 31, column: 13} - - {line: 39, column: 13} - - {line: 34, column: 13} - - {line: 42, column: 13} -- name: return types must be unambiguous/disallows differing return type nullability despite no overlap - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ... on NonNullStringBox1 { - scalar - } - ... on StringBox { - scalar - } - } - } - - errors: - - message: Fields "scalar" conflict because they return conflicting types "String!" and "String". Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 5, column: 17} - - {line: 8, column: 17} -- name: return types must be unambiguous/disallows differing return type list despite no overlap - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ... on IntBox { - box: listStringBox { - scalar - } - } - ... on StringBox { - box: stringBox { - scalar - } - } - } - } - - errors: - - message: Fields "box" conflict because they return conflicting types "[StringBox]" and "StringBox". Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 5, column: 17} - - {line: 10, column: 17} -- name: return types must be unambiguous/disallows differing return type list despite no overlap - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ... on IntBox { - box: stringBox { - scalar - } - } - ... on StringBox { - box: listStringBox { - scalar - } - } - } - } - - errors: - - message: Fields "box" conflict because they return conflicting types "StringBox" and "[StringBox]". Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 5, column: 17} - - {line: 10, column: 17} -- name: return types must be unambiguous/disallows differing subfields - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ... on IntBox { - box: stringBox { - val: scalar - val: unrelatedField - } - } - ... on StringBox { - box: stringBox { - val: scalar - } - } - } - } - - errors: - - message: Fields "val" conflict because "scalar" and "unrelatedField" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 6, column: 19} - - {line: 7, column: 19} -- name: return types must be unambiguous/disallows differing deep return types despite no overlap - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ... on IntBox { - box: stringBox { - scalar - } - } - ... on StringBox { - box: intBox { - scalar - } - } - } - } - - errors: - - message: Fields "box" conflict because subfields "scalar" conflict because they return conflicting types "String" and "Int". Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 5, column: 17} - - {line: 6, column: 19} - - {line: 10, column: 17} - - {line: 11, column: 19} -- name: return types must be unambiguous/allows non-conflicting overlapping types - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ... on IntBox { - scalar: unrelatedField - } - ... on StringBox { - scalar - } - } - } - - errors: [] -- name: return types must be unambiguous/same wrapped scalar return types - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ...on NonNullStringBox1 { - scalar - } - ...on NonNullStringBox2 { - scalar - } - } - } - - errors: [] -- name: return types must be unambiguous/allows inline fragments without type condition - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - a - ... { - a - } - } - - errors: [] -- name: return types must be unambiguous/compares deep types including list - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - connection { - ...edgeID - edges { - node { - id: name - } - } - } - } - - fragment edgeID on Connection { - edges { - node { - id - } - } - } - - errors: - - message: Fields "edges" conflict because subfields "node" conflict because subfields "id" conflict because "name" and "id" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 5, column: 15} - - {line: 6, column: 17} - - {line: 7, column: 19} - - {line: 14, column: 13} - - {line: 15, column: 15} - - {line: 16, column: 17} -- name: return types must be unambiguous/ignores unknown types - rule: OverlappingFieldsCanBeMerged - schema: 12 - query: |2- - - { - someBox { - ...on UnknownType { - scalar - } - ...on NonNullStringBox2 { - scalar - } - } - } - - errors: [] -- name: return types must be unambiguous/works for field names that are JS keywords - rule: OverlappingFieldsCanBeMerged - schema: 13 - query: |2- - - { - foo { - constructor - } - } - - errors: [] -- name: does not infinite loop on recursive fragment - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - ...fragA - } - - fragment fragA on Human { name, relatives { name, ...fragA } } - - errors: [] -- name: does not infinite loop on immediately recursive fragment - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - ...fragA - } - - fragment fragA on Human { name, ...fragA } - - errors: [] -- name: does not infinite loop on recursive fragment with a field named after fragment - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - ...fragA - fragA - } - - fragment fragA on Query { ...fragA } - - errors: [] -- name: finds invalid cases even with field named after fragment - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - fragA - ...fragA - } - - fragment fragA on Type { - fragA: b - } - - errors: - - message: Fields "fragA" conflict because "fragA" and "b" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 3, column: 9} - - {line: 8, column: 9} -- name: does not infinite loop on transitively recursive fragment - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - { - ...fragA - fragB - } - - fragment fragA on Human { name, ...fragB } - fragment fragB on Human { name, ...fragC } - fragment fragC on Human { name, ...fragA } - - errors: [] -- name: finds invalid case even with immediately recursive fragment - rule: OverlappingFieldsCanBeMerged - schema: 0 - query: |2- - - fragment sameAliasesWithDifferentFieldTargets on Dog { - ...sameAliasesWithDifferentFieldTargets - fido: name - fido: nickname - } - - errors: - - message: Fields "fido" conflict because "name" and "nickname" are different fields. Use different aliases on the fields to fetch both if this was intentional. - locations: - - {line: 4, column: 9} - - {line: 5, column: 9} diff --git a/internal/gqlparser/validator/imported/spec/PossibleFragmentSpreadsRule.spec.yml b/internal/gqlparser/validator/imported/spec/PossibleFragmentSpreadsRule.spec.yml deleted file mode 100644 index 099c551206..0000000000 --- a/internal/gqlparser/validator/imported/spec/PossibleFragmentSpreadsRule.spec.yml +++ /dev/null @@ -1,250 +0,0 @@ -- name: of the same object - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment objectWithinObject on Dog { ...dogFragment } - fragment dogFragment on Dog { barkVolume } - - errors: [] -- name: of the same object with inline fragment - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } } - - errors: [] -- name: object into an implemented interface - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment objectWithinInterface on Pet { ...dogFragment } - fragment dogFragment on Dog { barkVolume } - - errors: [] -- name: object into containing union - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment objectWithinUnion on CatOrDog { ...dogFragment } - fragment dogFragment on Dog { barkVolume } - - errors: [] -- name: union into contained object - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment unionWithinObject on Dog { ...catOrDogFragment } - fragment catOrDogFragment on CatOrDog { __typename } - - errors: [] -- name: union into overlapping interface - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment unionWithinInterface on Pet { ...catOrDogFragment } - fragment catOrDogFragment on CatOrDog { __typename } - - errors: [] -- name: union into overlapping union - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment } - fragment catOrDogFragment on CatOrDog { __typename } - - errors: [] -- name: interface into implemented object - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment interfaceWithinObject on Dog { ...petFragment } - fragment petFragment on Pet { name } - - errors: [] -- name: interface into overlapping interface - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment interfaceWithinInterface on Pet { ...beingFragment } - fragment beingFragment on Being { name } - - errors: [] -- name: interface into overlapping interface in inline fragment - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment interfaceWithinInterface on Pet { ... on Being { name } } - - errors: [] -- name: interface into overlapping union - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment interfaceWithinUnion on CatOrDog { ...petFragment } - fragment petFragment on Pet { name } - - errors: [] -- name: ignores incorrect type (caught by FragmentsOnCompositeTypesRule) - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment petFragment on Pet { ...badInADifferentWay } - fragment badInADifferentWay on String { name } - - errors: [] -- name: ignores unknown fragments (caught by KnownFragmentNamesRule) - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment petFragment on Pet { ...UnknownFragment } - - errors: [] -- name: different object into object - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidObjectWithinObject on Cat { ...dogFragment } - fragment dogFragment on Dog { barkVolume } - - errors: - - message: Fragment "dogFragment" cannot be spread here as objects of type "Cat" can never be of type "Dog". - locations: - - {line: 2, column: 51} -- name: different object into object in inline fragment - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidObjectWithinObjectAnon on Cat { - ... on Dog { barkVolume } - } - - errors: - - message: Fragment cannot be spread here as objects of type "Cat" can never be of type "Dog". - locations: - - {line: 3, column: 9} -- name: object into not implementing interface - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidObjectWithinInterface on Pet { ...humanFragment } - fragment humanFragment on Human { pets { name } } - - errors: - - message: Fragment "humanFragment" cannot be spread here as objects of type "Pet" can never be of type "Human". - locations: - - {line: 2, column: 54} -- name: object into not containing union - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment } - fragment humanFragment on Human { pets { name } } - - errors: - - message: Fragment "humanFragment" cannot be spread here as objects of type "CatOrDog" can never be of type "Human". - locations: - - {line: 2, column: 55} -- name: union into not contained object - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidUnionWithinObject on Human { ...catOrDogFragment } - fragment catOrDogFragment on CatOrDog { __typename } - - errors: - - message: Fragment "catOrDogFragment" cannot be spread here as objects of type "Human" can never be of type "CatOrDog". - locations: - - {line: 2, column: 52} -- name: union into non overlapping interface - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment } - fragment humanOrAlienFragment on HumanOrAlien { __typename } - - errors: - - message: Fragment "humanOrAlienFragment" cannot be spread here as objects of type "Pet" can never be of type "HumanOrAlien". - locations: - - {line: 2, column: 53} -- name: union into non overlapping union - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment } - fragment humanOrAlienFragment on HumanOrAlien { __typename } - - errors: - - message: Fragment "humanOrAlienFragment" cannot be spread here as objects of type "CatOrDog" can never be of type "HumanOrAlien". - locations: - - {line: 2, column: 54} -- name: interface into non implementing object - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment } - fragment intelligentFragment on Intelligent { iq } - - errors: - - message: Fragment "intelligentFragment" cannot be spread here as objects of type "Cat" can never be of type "Intelligent". - locations: - - {line: 2, column: 54} -- name: interface into non overlapping interface - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidInterfaceWithinInterface on Pet { - ...intelligentFragment - } - fragment intelligentFragment on Intelligent { iq } - - errors: - - message: Fragment "intelligentFragment" cannot be spread here as objects of type "Pet" can never be of type "Intelligent". - locations: - - {line: 3, column: 9} -- name: interface into non overlapping interface in inline fragment - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidInterfaceWithinInterfaceAnon on Pet { - ...on Intelligent { iq } - } - - errors: - - message: Fragment cannot be spread here as objects of type "Pet" can never be of type "Intelligent". - locations: - - {line: 3, column: 9} -- name: interface into non overlapping union - rule: PossibleFragmentSpreads - schema: 14 - query: |2- - - fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment } - fragment petFragment on Pet { name } - - errors: - - message: Fragment "petFragment" cannot be spread here as objects of type "HumanOrAlien" can never be of type "Pet". - locations: - - {line: 2, column: 62} diff --git a/internal/gqlparser/validator/imported/spec/PossibleTypeExtensionsRule.spec.yml b/internal/gqlparser/validator/imported/spec/PossibleTypeExtensionsRule.spec.yml deleted file mode 100644 index fe51488c70..0000000000 --- a/internal/gqlparser/validator/imported/spec/PossibleTypeExtensionsRule.spec.yml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/internal/gqlparser/validator/imported/spec/ProvidedRequiredArgumentsRule.spec.yml b/internal/gqlparser/validator/imported/spec/ProvidedRequiredArgumentsRule.spec.yml deleted file mode 100644 index f5369bc1b0..0000000000 --- a/internal/gqlparser/validator/imported/spec/ProvidedRequiredArgumentsRule.spec.yml +++ /dev/null @@ -1,235 +0,0 @@ -- name: ignores unknown arguments - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - dog { - isHouseTrained(unknownArgument: true) - } - } - - errors: [] -- name: Valid non-nullable value/Arg on optional arg - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - dog { - isHouseTrained(atOtherHomes: true) - } - } - - errors: [] -- name: Valid non-nullable value/No Arg on optional arg - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - dog { - isHouseTrained - } - } - - errors: [] -- name: Valid non-nullable value/No arg on non-null field with default - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - nonNullFieldWithDefault - } - } - - errors: [] -- name: Valid non-nullable value/Multiple args - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs(req1: 1, req2: 2) - } - } - - errors: [] -- name: Valid non-nullable value/Multiple args reverse order - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs(req2: 2, req1: 1) - } - } - - errors: [] -- name: Valid non-nullable value/No args on multiple optional - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOpts - } - } - - errors: [] -- name: Valid non-nullable value/One arg on multiple optional - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOpts(opt1: 1) - } - } - - errors: [] -- name: Valid non-nullable value/Second arg on multiple optional - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOpts(opt2: 1) - } - } - - errors: [] -- name: Valid non-nullable value/Multiple required args on mixedList - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOptAndReq(req1: 3, req2: 4) - } - } - - errors: [] -- name: Valid non-nullable value/Multiple required and one optional arg on mixedList - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOptAndReq(req1: 3, req2: 4, opt1: 5) - } - } - - errors: [] -- name: Valid non-nullable value/All required and optional args on mixedList - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6) - } - } - - errors: [] -- name: Invalid non-nullable value/Missing one non-nullable argument - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs(req2: 2) - } - } - - errors: - - message: Field "multipleReqs" argument "req1" of type "Int!" is required, but it was not provided. - locations: - - {line: 4, column: 13} -- name: Invalid non-nullable value/Missing multiple non-nullable arguments - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs - } - } - - errors: - - message: Field "multipleReqs" argument "req1" of type "Int!" is required, but it was not provided. - locations: - - {line: 4, column: 13} - - message: Field "multipleReqs" argument "req2" of type "Int!" is required, but it was not provided. - locations: - - {line: 4, column: 13} -- name: Invalid non-nullable value/Incorrect value and missing argument - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs(req1: "one") - } - } - - errors: - - message: Field "multipleReqs" argument "req2" of type "Int!" is required, but it was not provided. - locations: - - {line: 4, column: 13} -- name: Directive arguments/ignores unknown directives - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - dog @unknown - } - - errors: [] -- name: Directive arguments/with directives of valid types - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - dog @include(if: true) { - name - } - human @skip(if: false) { - name - } - } - - errors: [] -- name: Directive arguments/with directive with missing types - rule: ProvidedRequiredArguments - schema: 0 - query: |2- - - { - dog @include { - name @skip - } - } - - errors: - - message: Directive "@include" argument "if" of type "Boolean!" is required, but it was not provided. - locations: - - {line: 3, column: 15} - - message: Directive "@skip" argument "if" of type "Boolean!" is required, but it was not provided. - locations: - - {line: 4, column: 18} diff --git a/internal/gqlparser/validator/imported/spec/ScalarLeafsRule.spec.yml b/internal/gqlparser/validator/imported/spec/ScalarLeafsRule.spec.yml deleted file mode 100644 index 7a7a521fb8..0000000000 --- a/internal/gqlparser/validator/imported/spec/ScalarLeafsRule.spec.yml +++ /dev/null @@ -1,111 +0,0 @@ -- name: valid scalar selection - rule: ScalarLeafs - schema: 0 - query: |2- - - fragment scalarSelection on Dog { - barks - } - - errors: [] -- name: object type missing selection - rule: ScalarLeafs - schema: 0 - query: |2- - - query directQueryOnObjectWithoutSubFields { - human - } - - errors: - - message: Field "human" of type "Human" must have a selection of subfields. Did you mean "human { ... }"? - locations: - - {line: 3, column: 9} -- name: interface type missing selection - rule: ScalarLeafs - schema: 0 - query: |2- - - { - human { pets } - } - - errors: - - message: Field "pets" of type "[Pet]" must have a selection of subfields. Did you mean "pets { ... }"? - locations: - - {line: 3, column: 17} -- name: valid scalar selection with args - rule: ScalarLeafs - schema: 0 - query: |2- - - fragment scalarSelectionWithArgs on Dog { - doesKnowCommand(dogCommand: SIT) - } - - errors: [] -- name: scalar selection not allowed on Boolean - rule: ScalarLeafs - schema: 0 - query: |2- - - fragment scalarSelectionsNotAllowedOnBoolean on Dog { - barks { sinceWhen } - } - - errors: - - message: Field "barks" must not have a selection since type "Boolean" has no subfields. - locations: - - {line: 3, column: 15} -- name: scalar selection not allowed on Enum - rule: ScalarLeafs - schema: 0 - query: |2- - - fragment scalarSelectionsNotAllowedOnEnum on Cat { - furColor { inHexDec } - } - - errors: - - message: Field "furColor" must not have a selection since type "FurColor" has no subfields. - locations: - - {line: 3, column: 18} -- name: scalar selection not allowed with args - rule: ScalarLeafs - schema: 0 - query: |2- - - fragment scalarSelectionsNotAllowedWithArgs on Dog { - doesKnowCommand(dogCommand: SIT) { sinceWhen } - } - - errors: - - message: Field "doesKnowCommand" must not have a selection since type "Boolean" has no subfields. - locations: - - {line: 3, column: 42} -- name: Scalar selection not allowed with directives - rule: ScalarLeafs - schema: 0 - query: |2- - - fragment scalarSelectionsNotAllowedWithDirectives on Dog { - name @include(if: true) { isAlsoHumanName } - } - - errors: - - message: Field "name" must not have a selection since type "String" has no subfields. - locations: - - {line: 3, column: 33} -- name: Scalar selection not allowed with directives and args - rule: ScalarLeafs - schema: 0 - query: |2- - - fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog { - doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen } - } - - errors: - - message: Field "doesKnowCommand" must not have a selection since type "Boolean" has no subfields. - locations: - - {line: 3, column: 61} diff --git a/internal/gqlparser/validator/imported/spec/SingleFieldSubscriptionsRule.spec.yml b/internal/gqlparser/validator/imported/spec/SingleFieldSubscriptionsRule.spec.yml deleted file mode 100644 index ad715545e2..0000000000 --- a/internal/gqlparser/validator/imported/spec/SingleFieldSubscriptionsRule.spec.yml +++ /dev/null @@ -1,241 +0,0 @@ -- name: valid subscription - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription ImportantEmails { - importantEmails - } - - errors: [] -- name: valid subscription with fragment - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription sub { - ...newMessageFields - } - - fragment newMessageFields on SubscriptionRoot { - newMessage { - body - sender - } - } - - errors: [] -- name: valid subscription with fragment and field - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription sub { - newMessage { - body - } - ...newMessageFields - } - - fragment newMessageFields on SubscriptionRoot { - newMessage { - body - sender - } - } - - errors: [] -- name: fails with more than one root field - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription ImportantEmails { - importantEmails - notImportantEmails - } - - errors: - - message: Subscription "ImportantEmails" must select only one top level field. - locations: - - {line: 4, column: 9} -- name: fails with more than one root field including introspection - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription ImportantEmails { - importantEmails - __typename - } - - errors: - - message: Subscription "ImportantEmails" must select only one top level field. - locations: - - {line: 4, column: 9} - - message: Subscription "ImportantEmails" must not select an introspection top level field. - locations: - - {line: 4, column: 9} -- name: fails with more than one root field including aliased introspection via fragment - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription ImportantEmails { - importantEmails - ...Introspection - } - fragment Introspection on SubscriptionRoot { - typename: __typename - } - - errors: - - message: Subscription "ImportantEmails" must select only one top level field. - locations: - - {line: 7, column: 9} - - message: Subscription "ImportantEmails" must not select an introspection top level field. - locations: - - {line: 7, column: 9} -- name: fails with many more than one root field - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription ImportantEmails { - importantEmails - notImportantEmails - spamEmails - } - - errors: - - message: Subscription "ImportantEmails" must select only one top level field. - locations: - - {line: 4, column: 9} - - {line: 5, column: 9} -- name: fails with many more than one root field via fragments - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription ImportantEmails { - importantEmails - ... { - more: moreImportantEmails - } - ...NotImportantEmails - } - fragment NotImportantEmails on SubscriptionRoot { - notImportantEmails - deleted: deletedEmails - ...SpamEmails - } - fragment SpamEmails on SubscriptionRoot { - spamEmails - } - - errors: - - message: Subscription "ImportantEmails" must select only one top level field. - locations: - - {line: 5, column: 11} - - {line: 10, column: 9} - - {line: 11, column: 9} - - {line: 15, column: 9} -- name: does not infinite loop on recursive fragments - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription NoInfiniteLoop { - ...A - } - fragment A on SubscriptionRoot { - ...A - } - - errors: [] -- name: fails with many more than one root field via fragments (anonymous) - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription { - importantEmails - ... { - more: moreImportantEmails - ...NotImportantEmails - } - ...NotImportantEmails - } - fragment NotImportantEmails on SubscriptionRoot { - notImportantEmails - deleted: deletedEmails - ... { - ... { - archivedEmails - } - } - ...SpamEmails - } - fragment SpamEmails on SubscriptionRoot { - spamEmails - ...NonExistentFragment - } - - errors: - - message: Anonymous Subscription must select only one top level field. - locations: - - {line: 5, column: 11} - - {line: 11, column: 9} - - {line: 12, column: 9} - - {line: 15, column: 13} - - {line: 21, column: 9} -- name: fails with more than one root field in anonymous subscriptions - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription { - importantEmails - notImportantEmails - } - - errors: - - message: Anonymous Subscription must select only one top level field. - locations: - - {line: 4, column: 9} -- name: fails with introspection field - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription ImportantEmails { - __typename - } - - errors: - - message: Subscription "ImportantEmails" must not select an introspection top level field. - locations: - - {line: 3, column: 9} -- name: fails with introspection field in anonymous subscription - rule: SingleFieldSubscriptions - schema: 15 - query: |2- - - subscription { - __typename - } - - errors: - - message: Anonymous Subscription must not select an introspection top level field. - locations: - - {line: 3, column: 9} -- name: skips if not subscription type - rule: SingleFieldSubscriptions - schema: 16 - query: |2- - - subscription { - __typename - } - - errors: [] diff --git a/internal/gqlparser/validator/imported/spec/UniqueArgumentDefinitionNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueArgumentDefinitionNamesRule.spec.yml deleted file mode 100644 index fe51488c70..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueArgumentDefinitionNamesRule.spec.yml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/internal/gqlparser/validator/imported/spec/UniqueArgumentNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueArgumentNamesRule.spec.yml deleted file mode 100644 index f3553cce25..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueArgumentNamesRule.spec.yml +++ /dev/null @@ -1,149 +0,0 @@ -- name: no arguments on field - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field - } - - errors: [] -- name: no arguments on directive - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field @directive - } - - errors: [] -- name: argument on field - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field(arg: "value") - } - - errors: [] -- name: argument on directive - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field @directive(arg: "value") - } - - errors: [] -- name: same argument on two fields - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - one: field(arg: "value") - two: field(arg: "value") - } - - errors: [] -- name: same argument on field and directive - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field(arg: "value") @directive(arg: "value") - } - - errors: [] -- name: same argument on two directives - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field @directive1(arg: "value") @directive2(arg: "value") - } - - errors: [] -- name: multiple field arguments - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field(arg1: "value", arg2: "value", arg3: "value") - } - - errors: [] -- name: multiple directive arguments - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field @directive(arg1: "value", arg2: "value", arg3: "value") - } - - errors: [] -- name: duplicate field arguments - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field(arg1: "value", arg1: "value") - } - - errors: - - message: There can be only one argument named "arg1". - locations: - - {line: 3, column: 15} - - {line: 3, column: 30} -- name: many duplicate field arguments - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field(arg1: "value", arg1: "value", arg1: "value") - } - - errors: - - message: There can be only one argument named "arg1". - locations: - - {line: 3, column: 15} - - {line: 3, column: 30} - - {line: 3, column: 45} -- name: duplicate directive arguments - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field @directive(arg1: "value", arg1: "value") - } - - errors: - - message: There can be only one argument named "arg1". - locations: - - {line: 3, column: 26} - - {line: 3, column: 41} -- name: many duplicate directive arguments - rule: UniqueArgumentNames - schema: 0 - query: |2- - - { - field @directive(arg1: "value", arg1: "value", arg1: "value") - } - - errors: - - message: There can be only one argument named "arg1". - locations: - - {line: 3, column: 26} - - {line: 3, column: 41} - - {line: 3, column: 56} diff --git a/internal/gqlparser/validator/imported/spec/UniqueDirectiveNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueDirectiveNamesRule.spec.yml deleted file mode 100644 index fe51488c70..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueDirectiveNamesRule.spec.yml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/internal/gqlparser/validator/imported/spec/UniqueDirectivesPerLocationRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueDirectivesPerLocationRule.spec.yml deleted file mode 100644 index 81acb06687..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueDirectivesPerLocationRule.spec.yml +++ /dev/null @@ -1,143 +0,0 @@ -- name: no directives - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type { - field - } - - errors: [] -- name: unique directives in different locations - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type @directiveA { - field @directiveB - } - - errors: [] -- name: unique directives in same locations - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type @directiveA @directiveB { - field @directiveA @directiveB - } - - errors: [] -- name: same directives in different locations - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type @directiveA { - field @directiveA - } - - errors: [] -- name: same directives in similar locations - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type { - field @directive - field @directive - } - - errors: [] -- name: repeatable directives in same location - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type @repeatable @repeatable { - field @repeatable @repeatable - } - - errors: [] -- name: unknown directives must be ignored - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - type Test @unknown @unknown { - field: String! @unknown @unknown - } - - extend type Test @unknown { - anotherField: String! - } - - errors: [] -- name: duplicate directives in one location - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type { - field @directive @directive - } - - errors: - - message: The directive "@directive" can only be used once at this location. - locations: - - {line: 3, column: 15} - - {line: 3, column: 26} -- name: many duplicate directives in one location - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type { - field @directive @directive @directive - } - - errors: - - message: The directive "@directive" can only be used once at this location. - locations: - - {line: 3, column: 15} - - {line: 3, column: 26} - - message: The directive "@directive" can only be used once at this location. - locations: - - {line: 3, column: 15} - - {line: 3, column: 37} -- name: different duplicate directives in one location - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type { - field @directiveA @directiveB @directiveA @directiveB - } - - errors: - - message: The directive "@directiveA" can only be used once at this location. - locations: - - {line: 3, column: 15} - - {line: 3, column: 39} - - message: The directive "@directiveB" can only be used once at this location. - locations: - - {line: 3, column: 27} - - {line: 3, column: 51} -- name: duplicate directives in many locations - rule: UniqueDirectivesPerLocation - schema: 17 - query: |2- - - fragment Test on Type @directive @directive { - field @directive @directive - } - - errors: - - message: The directive "@directive" can only be used once at this location. - locations: - - {line: 2, column: 29} - - {line: 2, column: 40} - - message: The directive "@directive" can only be used once at this location. - locations: - - {line: 3, column: 15} - - {line: 3, column: 26} diff --git a/internal/gqlparser/validator/imported/spec/UniqueEnumValueNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueEnumValueNamesRule.spec.yml deleted file mode 100644 index fe51488c70..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueEnumValueNamesRule.spec.yml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/internal/gqlparser/validator/imported/spec/UniqueFieldDefinitionNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueFieldDefinitionNamesRule.spec.yml deleted file mode 100644 index fe51488c70..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueFieldDefinitionNamesRule.spec.yml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/internal/gqlparser/validator/imported/spec/UniqueFragmentNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueFragmentNamesRule.spec.yml deleted file mode 100644 index 4d294f0c20..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueFragmentNamesRule.spec.yml +++ /dev/null @@ -1,110 +0,0 @@ -- name: no fragments - rule: UniqueFragmentNames - schema: 0 - query: |2- - - { - field - } - - errors: [] -- name: one fragment - rule: UniqueFragmentNames - schema: 0 - query: |2- - - { - ...fragA - } - - fragment fragA on Type { - field - } - - errors: [] -- name: many fragments - rule: UniqueFragmentNames - schema: 0 - query: |2- - - { - ...fragA - ...fragB - ...fragC - } - fragment fragA on Type { - fieldA - } - fragment fragB on Type { - fieldB - } - fragment fragC on Type { - fieldC - } - - errors: [] -- name: inline fragments are always unique - rule: UniqueFragmentNames - schema: 0 - query: |2- - - { - ...on Type { - fieldA - } - ...on Type { - fieldB - } - } - - errors: [] -- name: fragment and operation named the same - rule: UniqueFragmentNames - schema: 0 - query: |2- - - query Foo { - ...Foo - } - fragment Foo on Type { - field - } - - errors: [] -- name: fragments named the same - rule: UniqueFragmentNames - schema: 0 - query: |2- - - { - ...fragA - } - fragment fragA on Type { - fieldA - } - fragment fragA on Type { - fieldB - } - - errors: - - message: There can be only one fragment named "fragA". - locations: - - {line: 5, column: 16} - - {line: 8, column: 16} -- name: fragments named the same without being referenced - rule: UniqueFragmentNames - schema: 0 - query: |2- - - fragment fragA on Type { - fieldA - } - fragment fragA on Type { - fieldB - } - - errors: - - message: There can be only one fragment named "fragA". - locations: - - {line: 2, column: 16} - - {line: 5, column: 16} diff --git a/internal/gqlparser/validator/imported/spec/UniqueInputFieldNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueInputFieldNamesRule.spec.yml deleted file mode 100644 index 5f28dd6ae1..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueInputFieldNamesRule.spec.yml +++ /dev/null @@ -1,94 +0,0 @@ -- name: input object with fields - rule: UniqueInputFieldNames - schema: 0 - query: |2- - - { - field(arg: { f: true }) - } - - errors: [] -- name: same input object within two args - rule: UniqueInputFieldNames - schema: 0 - query: |2- - - { - field(arg1: { f: true }, arg2: { f: true }) - } - - errors: [] -- name: multiple input object fields - rule: UniqueInputFieldNames - schema: 0 - query: |2- - - { - field(arg: { f1: "value", f2: "value", f3: "value" }) - } - - errors: [] -- name: allows for nested input objects with similar fields - rule: UniqueInputFieldNames - schema: 0 - query: |2- - - { - field(arg: { - deep: { - deep: { - id: 1 - } - id: 1 - } - id: 1 - }) - } - - errors: [] -- name: duplicate input object fields - rule: UniqueInputFieldNames - schema: 0 - query: |2- - - { - field(arg: { f1: "value", f1: "value" }) - } - - errors: - - message: There can be only one input field named "f1". - locations: - - {line: 3, column: 22} - - {line: 3, column: 35} -- name: many duplicate input object fields - rule: UniqueInputFieldNames - schema: 0 - query: |2- - - { - field(arg: { f1: "value", f1: "value", f1: "value" }) - } - - errors: - - message: There can be only one input field named "f1". - locations: - - {line: 3, column: 22} - - {line: 3, column: 35} - - message: There can be only one input field named "f1". - locations: - - {line: 3, column: 22} - - {line: 3, column: 48} -- name: nested duplicate input object fields - rule: UniqueInputFieldNames - schema: 0 - query: |2- - - { - field(arg: { f1: {f2: "value", f2: "value" }}) - } - - errors: - - message: There can be only one input field named "f2". - locations: - - {line: 3, column: 27} - - {line: 3, column: 40} diff --git a/internal/gqlparser/validator/imported/spec/UniqueOperationNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueOperationNamesRule.spec.yml deleted file mode 100644 index 195803eb8a..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueOperationNamesRule.spec.yml +++ /dev/null @@ -1,126 +0,0 @@ -- name: no operations - rule: UniqueOperationNames - schema: 0 - query: |2- - - fragment fragA on Type { - field - } - - errors: [] -- name: one anon operation - rule: UniqueOperationNames - schema: 0 - query: |2- - - { - field - } - - errors: [] -- name: one named operation - rule: UniqueOperationNames - schema: 0 - query: |2- - - query Foo { - field - } - - errors: [] -- name: multiple operations - rule: UniqueOperationNames - schema: 0 - query: |2- - - query Foo { - field - } - - query Bar { - field - } - - errors: [] -- name: multiple operations of different types - rule: UniqueOperationNames - schema: 0 - query: |2- - - query Foo { - field - } - - mutation Bar { - field - } - - subscription Baz { - field - } - - errors: [] -- name: fragment and operation named the same - rule: UniqueOperationNames - schema: 0 - query: |2- - - query Foo { - ...Foo - } - fragment Foo on Type { - field - } - - errors: [] -- name: multiple operations of same name - rule: UniqueOperationNames - schema: 0 - query: |2- - - query Foo { - fieldA - } - query Foo { - fieldB - } - - errors: - - message: There can be only one operation named "Foo". - locations: - - {line: 2, column: 13} - - {line: 5, column: 13} -- name: multiple ops of same name of different types (mutation) - rule: UniqueOperationNames - schema: 0 - query: |2- - - query Foo { - fieldA - } - mutation Foo { - fieldB - } - - errors: - - message: There can be only one operation named "Foo". - locations: - - {line: 2, column: 13} - - {line: 5, column: 16} -- name: multiple ops of same name of different types (subscription) - rule: UniqueOperationNames - schema: 0 - query: |2- - - query Foo { - fieldA - } - subscription Foo { - fieldB - } - - errors: - - message: There can be only one operation named "Foo". - locations: - - {line: 2, column: 13} - - {line: 5, column: 20} diff --git a/internal/gqlparser/validator/imported/spec/UniqueOperationTypesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueOperationTypesRule.spec.yml deleted file mode 100644 index fe51488c70..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueOperationTypesRule.spec.yml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/internal/gqlparser/validator/imported/spec/UniqueTypeNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueTypeNamesRule.spec.yml deleted file mode 100644 index fe51488c70..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueTypeNamesRule.spec.yml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/internal/gqlparser/validator/imported/spec/UniqueVariableNamesRule.spec.yml b/internal/gqlparser/validator/imported/spec/UniqueVariableNamesRule.spec.yml deleted file mode 100644 index 8a380da909..0000000000 --- a/internal/gqlparser/validator/imported/spec/UniqueVariableNamesRule.spec.yml +++ /dev/null @@ -1,32 +0,0 @@ -- name: unique variable names - rule: UniqueVariableNames - schema: 0 - query: |2- - - query A($x: Int, $y: String) { __typename } - query B($x: String, $y: Int) { __typename } - - errors: [] -- name: duplicate variable names - rule: UniqueVariableNames - schema: 0 - query: |2- - - query A($x: Int, $x: Int, $x: String) { __typename } - query B($x: String, $x: Int) { __typename } - query C($x: Int, $x: Int) { __typename } - - errors: - - message: There can be only one variable named "$x". - locations: - - {line: 2, column: 16} - - {line: 2, column: 25} - - {line: 2, column: 34} - - message: There can be only one variable named "$x". - locations: - - {line: 3, column: 16} - - {line: 3, column: 28} - - message: There can be only one variable named "$x". - locations: - - {line: 4, column: 16} - - {line: 4, column: 25} diff --git a/internal/gqlparser/validator/imported/spec/ValidationContext.spec.yml b/internal/gqlparser/validator/imported/spec/ValidationContext.spec.yml deleted file mode 100644 index fe51488c70..0000000000 --- a/internal/gqlparser/validator/imported/spec/ValidationContext.spec.yml +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/internal/gqlparser/validator/imported/spec/ValuesOfCorrectTypeRule.spec.yml b/internal/gqlparser/validator/imported/spec/ValuesOfCorrectTypeRule.spec.yml deleted file mode 100644 index 83990de5d5..0000000000 --- a/internal/gqlparser/validator/imported/spec/ValuesOfCorrectTypeRule.spec.yml +++ /dev/null @@ -1,1115 +0,0 @@ -- name: Valid values/Good int value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - intArgField(intArg: 2) - } - } - - errors: [] -- name: Valid values/Good negative int value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - intArgField(intArg: -2) - } - } - - errors: [] -- name: Valid values/Good boolean value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - booleanArgField(booleanArg: true) - } - } - - errors: [] -- name: Valid values/Good string value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringArgField(stringArg: "foo") - } - } - - errors: [] -- name: Valid values/Good float value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - floatArgField(floatArg: 1.1) - } - } - - errors: [] -- name: Valid values/Good negative float value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - floatArgField(floatArg: -1.1) - } - } - - errors: [] -- name: Valid values/Int into Float - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - floatArgField(floatArg: 1) - } - } - - errors: [] -- name: Valid values/Int into ID - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - idArgField(idArg: 1) - } - } - - errors: [] -- name: Valid values/String into ID - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - idArgField(idArg: "someIdString") - } - } - - errors: [] -- name: Valid values/Good enum value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog { - doesKnowCommand(dogCommand: SIT) - } - } - - errors: [] -- name: Valid values/Enum with undefined value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - enumArgField(enumArg: UNKNOWN) - } - } - - errors: [] -- name: Valid values/Enum with null value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - enumArgField(enumArg: NO_FUR) - } - } - - errors: [] -- name: Valid values/null into nullable type - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - intArgField(intArg: null) - } - } - - errors: [] -- name: Valid values/null into nullable type - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog(a: null, b: null, c:{ requiredField: true, intField: null }) { - name - } - } - - errors: [] -- name: Invalid String values/Int into String - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringArgField(stringArg: 1) - } - } - - errors: - - message: 'String cannot represent a non string value: 1' - locations: - - {line: 4, column: 39} -- name: Invalid String values/Float into String - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringArgField(stringArg: 1.0) - } - } - - errors: - - message: 'String cannot represent a non string value: 1.0' - locations: - - {line: 4, column: 39} -- name: Invalid String values/Boolean into String - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringArgField(stringArg: true) - } - } - - errors: - - message: 'String cannot represent a non string value: true' - locations: - - {line: 4, column: 39} -- name: Invalid String values/Unquoted String into String - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringArgField(stringArg: BAR) - } - } - - errors: - - message: 'String cannot represent a non string value: BAR' - locations: - - {line: 4, column: 39} -- name: Invalid Int values/String into Int - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - intArgField(intArg: "3") - } - } - - errors: - - message: 'Int cannot represent non-integer value: "3"' - locations: - - {line: 4, column: 33} -- name: Invalid Int values/Big Int into Int - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - intArgField(intArg: 829384293849283498239482938) - } - } - - errors: - - message: 'Int cannot represent non 32-bit signed integer value: 829384293849283498239482938' - locations: - - {line: 4, column: 33} -- name: Invalid Int values/Unquoted String into Int - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - intArgField(intArg: FOO) - } - } - - errors: - - message: 'Int cannot represent non-integer value: FOO' - locations: - - {line: 4, column: 33} -- name: Invalid Int values/Simple Float into Int - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - intArgField(intArg: 3.0) - } - } - - errors: - - message: 'Int cannot represent non-integer value: 3.0' - locations: - - {line: 4, column: 33} -- name: Invalid Int values/Float into Int - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - intArgField(intArg: 3.333) - } - } - - errors: - - message: 'Int cannot represent non-integer value: 3.333' - locations: - - {line: 4, column: 33} -- name: Invalid Float values/String into Float - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - floatArgField(floatArg: "3.333") - } - } - - errors: - - message: 'Float cannot represent non numeric value: "3.333"' - locations: - - {line: 4, column: 37} -- name: Invalid Float values/Boolean into Float - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - floatArgField(floatArg: true) - } - } - - errors: - - message: 'Float cannot represent non numeric value: true' - locations: - - {line: 4, column: 37} -- name: Invalid Float values/Unquoted into Float - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - floatArgField(floatArg: FOO) - } - } - - errors: - - message: 'Float cannot represent non numeric value: FOO' - locations: - - {line: 4, column: 37} -- name: Invalid Boolean value/Int into Boolean - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - booleanArgField(booleanArg: 2) - } - } - - errors: - - message: 'Boolean cannot represent a non boolean value: 2' - locations: - - {line: 4, column: 41} -- name: Invalid Boolean value/Float into Boolean - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - booleanArgField(booleanArg: 1.0) - } - } - - errors: - - message: 'Boolean cannot represent a non boolean value: 1.0' - locations: - - {line: 4, column: 41} -- name: Invalid Boolean value/String into Boolean - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - booleanArgField(booleanArg: "true") - } - } - - errors: - - message: 'Boolean cannot represent a non boolean value: "true"' - locations: - - {line: 4, column: 41} -- name: Invalid Boolean value/Unquoted into Boolean - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - booleanArgField(booleanArg: TRUE) - } - } - - errors: - - message: 'Boolean cannot represent a non boolean value: TRUE' - locations: - - {line: 4, column: 41} -- name: Invalid ID value/Float into ID - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - idArgField(idArg: 1.0) - } - } - - errors: - - message: 'ID cannot represent a non-string and non-integer value: 1.0' - locations: - - {line: 4, column: 31} -- name: Invalid ID value/Boolean into ID - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - idArgField(idArg: true) - } - } - - errors: - - message: 'ID cannot represent a non-string and non-integer value: true' - locations: - - {line: 4, column: 31} -- name: Invalid ID value/Unquoted into ID - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - idArgField(idArg: SOMETHING) - } - } - - errors: - - message: 'ID cannot represent a non-string and non-integer value: SOMETHING' - locations: - - {line: 4, column: 31} -- name: Invalid Enum value/Int into Enum - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog { - doesKnowCommand(dogCommand: 2) - } - } - - errors: - - message: 'Enum "DogCommand" cannot represent non-enum value: 2.' - locations: - - {line: 4, column: 41} -- name: Invalid Enum value/Float into Enum - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog { - doesKnowCommand(dogCommand: 1.0) - } - } - - errors: - - message: 'Enum "DogCommand" cannot represent non-enum value: 1.0.' - locations: - - {line: 4, column: 41} -- name: Invalid Enum value/String into Enum - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog { - doesKnowCommand(dogCommand: "SIT") - } - } - - errors: - - message: 'Enum "DogCommand" cannot represent non-enum value: "SIT". Did you mean the enum value "SIT"?' - locations: - - {line: 4, column: 41} -- name: Invalid Enum value/Boolean into Enum - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog { - doesKnowCommand(dogCommand: true) - } - } - - errors: - - message: 'Enum "DogCommand" cannot represent non-enum value: true.' - locations: - - {line: 4, column: 41} -- name: Invalid Enum value/Unknown Enum Value into Enum - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog { - doesKnowCommand(dogCommand: JUGGLE) - } - } - - errors: - - message: Value "JUGGLE" does not exist in "DogCommand" enum. - locations: - - {line: 4, column: 41} -- name: Invalid Enum value/Different case Enum Value into Enum - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog { - doesKnowCommand(dogCommand: sit) - } - } - - errors: - - message: Value "sit" does not exist in "DogCommand" enum. Did you mean the enum value "SIT"? - locations: - - {line: 4, column: 41} -- name: Valid List value/Good list value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringListArgField(stringListArg: ["one", null, "two"]) - } - } - - errors: [] -- name: Valid List value/Empty list value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringListArgField(stringListArg: []) - } - } - - errors: [] -- name: Valid List value/Null value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringListArgField(stringListArg: null) - } - } - - errors: [] -- name: Valid List value/Single value into List - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringListArgField(stringListArg: "one") - } - } - - errors: [] -- name: Invalid List value/Incorrect item type - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringListArgField(stringListArg: ["one", 2]) - } - } - - errors: - - message: 'String cannot represent a non string value: 2' - locations: - - {line: 4, column: 55} -- name: Invalid List value/Single value of incorrect type - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - stringListArgField(stringListArg: 1) - } - } - - errors: - - message: 'String cannot represent a non string value: 1' - locations: - - {line: 4, column: 47} -- name: Valid non-nullable value/Arg on optional arg - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog { - isHouseTrained(atOtherHomes: true) - } - } - - errors: [] -- name: Valid non-nullable value/No Arg on optional arg - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog { - isHouseTrained - } - } - - errors: [] -- name: Valid non-nullable value/Multiple args - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs(req1: 1, req2: 2) - } - } - - errors: [] -- name: Valid non-nullable value/Multiple args reverse order - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs(req2: 2, req1: 1) - } - } - - errors: [] -- name: Valid non-nullable value/No args on multiple optional - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOpts - } - } - - errors: [] -- name: Valid non-nullable value/One arg on multiple optional - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOpts(opt1: 1) - } - } - - errors: [] -- name: Valid non-nullable value/Second arg on multiple optional - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOpts(opt2: 1) - } - } - - errors: [] -- name: Valid non-nullable value/Multiple required args on mixedList - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOptAndReq(req1: 3, req2: 4) - } - } - - errors: [] -- name: Valid non-nullable value/Multiple required and one optional arg on mixedList - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOptAndReq(req1: 3, req2: 4, opt1: 5) - } - } - - errors: [] -- name: Valid non-nullable value/All required and optional args on mixedList - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6) - } - } - - errors: [] -- name: Invalid non-nullable value/Incorrect value type - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs(req2: "two", req1: "one") - } - } - - errors: - - message: 'Int cannot represent non-integer value: "two"' - locations: - - {line: 4, column: 32} - - message: 'Int cannot represent non-integer value: "one"' - locations: - - {line: 4, column: 45} -- name: Invalid non-nullable value/Incorrect value and missing argument (ProvidedRequiredArgumentsRule) - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs(req1: "one") - } - } - - errors: - - message: 'Int cannot represent non-integer value: "one"' - locations: - - {line: 4, column: 32} -- name: Invalid non-nullable value/Null value - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - multipleReqs(req1: null) - } - } - - errors: - - message: Expected value of type "Int!", found null. - locations: - - {line: 4, column: 32} -- name: Valid input object value/Optional arg, despite required field in type - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField - } - } - - errors: [] -- name: Valid input object value/Partial object, only required - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField(complexArg: { requiredField: true }) - } - } - - errors: [] -- name: Valid input object value/Partial object, required field can be falsy - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField(complexArg: { requiredField: false }) - } - } - - errors: [] -- name: Valid input object value/Partial object, including required - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField(complexArg: { requiredField: true, intField: 4 }) - } - } - - errors: [] -- name: Valid input object value/Full object - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField(complexArg: { - requiredField: true, - intField: 4, - stringField: "foo", - booleanField: false, - stringListField: ["one", "two"] - }) - } - } - - errors: [] -- name: Valid input object value/Full object with fields in different order - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField(complexArg: { - stringListField: ["one", "two"], - booleanField: false, - requiredField: true, - stringField: "foo", - intField: 4, - }) - } - } - - errors: [] -- name: Invalid input object value/Partial object, missing required - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField(complexArg: { intField: 4 }) - } - } - - errors: - - message: Field "ComplexInput.requiredField" of required type "Boolean!" was not provided. - locations: - - {line: 4, column: 41} -- name: Invalid input object value/Partial object, invalid field type - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField(complexArg: { - stringListField: ["one", 2], - requiredField: true, - }) - } - } - - errors: - - message: 'String cannot represent a non string value: 2' - locations: - - {line: 5, column: 40} -- name: Invalid input object value/Partial object, null to non-null field - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField(complexArg: { - requiredField: true, - nonNullField: null, - }) - } - } - - errors: - - message: Expected value of type "Boolean!", found null. - locations: - - {line: 6, column: 29} -- name: Invalid input object value/Partial object, unknown field arg - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - complicatedArgs { - complexArgField(complexArg: { - requiredField: true, - invalidField: "value" - }) - } - } - - errors: - - message: Field "invalidField" is not defined by type "ComplexInput". Did you mean "intField"? - locations: - - {line: 6, column: 15} -- name: Invalid input object value/reports error for custom scalar that returns undefined - rule: ValuesOfCorrectType - schema: 18 - query: '{ invalidArg(arg: 123) }' - errors: - - message: Expected value of type "CustomScalar", found 123. - locations: - - {line: 1, column: 19} -- name: Invalid input object value/allows custom scalar to accept complex literals - rule: ValuesOfCorrectType - schema: 19 - query: |2- - - { - test1: anyArg(arg: 123) - test2: anyArg(arg: "abc") - test3: anyArg(arg: [123, "abc"]) - test4: anyArg(arg: {deep: [123, "abc"]}) - } - - errors: [] -- name: Directive arguments/with directives of valid types - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog @include(if: true) { - name - } - human @skip(if: false) { - name - } - } - - errors: [] -- name: Directive arguments/with directive with incorrect types - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - { - dog @include(if: "yes") { - name @skip(if: ENUM) - } - } - - errors: - - message: 'Boolean cannot represent a non boolean value: "yes"' - locations: - - {line: 3, column: 28} - - message: 'Boolean cannot represent a non boolean value: ENUM' - locations: - - {line: 4, column: 28} -- name: Variable default values/variables with valid default values - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - query WithDefaultValues( - $a: Int = 1, - $b: String = "ok", - $c: ComplexInput = { requiredField: true, intField: 3 } - $d: Int! = 123 - ) { - dog { name } - } - - errors: [] -- name: Variable default values/variables with valid default null values - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - query WithDefaultValues( - $a: Int = null, - $b: String = null, - $c: ComplexInput = { requiredField: true, intField: null } - ) { - dog { name } - } - - errors: [] -- name: Variable default values/variables with invalid default null values - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - query WithDefaultValues( - $a: Int! = null, - $b: String! = null, - $c: ComplexInput = { requiredField: null, intField: null } - ) { - dog { name } - } - - errors: - - message: Expected value of type "Int!", found null. - locations: - - {line: 3, column: 22} - - message: Expected value of type "String!", found null. - locations: - - {line: 4, column: 25} - - message: Expected value of type "Boolean!", found null. - locations: - - {line: 5, column: 47} -- name: Variable default values/variables with invalid default values - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - query InvalidDefaultValues( - $a: Int = "one", - $b: String = 4, - $c: ComplexInput = "NotVeryComplex" - ) { - dog { name } - } - - errors: - - message: 'Int cannot represent non-integer value: "one"' - locations: - - {line: 3, column: 21} - - message: 'String cannot represent a non string value: 4' - locations: - - {line: 4, column: 24} - - message: Expected value of type "ComplexInput", found "NotVeryComplex". - locations: - - {line: 5, column: 30} -- name: Variable default values/variables with complex invalid default values - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - query WithDefaultValues( - $a: ComplexInput = { requiredField: 123, intField: "abc" } - ) { - dog { name } - } - - errors: - - message: 'Boolean cannot represent a non boolean value: 123' - locations: - - {line: 3, column: 47} - - message: 'Int cannot represent non-integer value: "abc"' - locations: - - {line: 3, column: 62} -- name: Variable default values/complex variables missing required field - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - query MissingRequiredField($a: ComplexInput = {intField: 3}) { - dog { name } - } - - errors: - - message: Field "ComplexInput.requiredField" of required type "Boolean!" was not provided. - locations: - - {line: 2, column: 55} -- name: Variable default values/list variables with invalid item - rule: ValuesOfCorrectType - schema: 0 - query: |2- - - query InvalidItem($a: [String] = ["one", 2]) { - dog { name } - } - - errors: - - message: 'String cannot represent a non string value: 2' - locations: - - {line: 2, column: 50} diff --git a/internal/gqlparser/validator/imported/spec/VariablesAreInputTypesRule.spec.yml b/internal/gqlparser/validator/imported/spec/VariablesAreInputTypesRule.spec.yml deleted file mode 100644 index be0813d4c0..0000000000 --- a/internal/gqlparser/validator/imported/spec/VariablesAreInputTypesRule.spec.yml +++ /dev/null @@ -1,39 +0,0 @@ -- name: unknown types are ignored - rule: VariablesAreInputTypes - schema: 0 - query: |2- - - query Foo($a: Unknown, $b: [[Unknown!]]!) { - field(a: $a, b: $b) - } - - errors: [] -- name: input types are valid - rule: VariablesAreInputTypes - schema: 0 - query: |2- - - query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) { - field(a: $a, b: $b, c: $c) - } - - errors: [] -- name: output types are invalid - rule: VariablesAreInputTypes - schema: 0 - query: |2- - - query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) { - field(a: $a, b: $b, c: $c) - } - - errors: - - locations: - - {line: 2, column: 21} - message: Variable "$a" cannot be non-input type "Dog". - - locations: - - {line: 2, column: 30} - message: Variable "$b" cannot be non-input type "[[CatOrDog!]]!". - - locations: - - {line: 2, column: 50} - message: Variable "$c" cannot be non-input type "Pet". diff --git a/internal/gqlparser/validator/imported/spec/VariablesInAllowedPositionRule.spec.yml b/internal/gqlparser/validator/imported/spec/VariablesInAllowedPositionRule.spec.yml deleted file mode 100644 index f9b48c957b..0000000000 --- a/internal/gqlparser/validator/imported/spec/VariablesInAllowedPositionRule.spec.yml +++ /dev/null @@ -1,348 +0,0 @@ -- name: Boolean => Boolean - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($booleanArg: Boolean) - { - complicatedArgs { - booleanArgField(booleanArg: $booleanArg) - } - } - - errors: [] -- name: Boolean => Boolean within fragment - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - fragment booleanArgFrag on ComplicatedArgs { - booleanArgField(booleanArg: $booleanArg) - } - query Query($booleanArg: Boolean) - { - complicatedArgs { - ...booleanArgFrag - } - } - - errors: [] -- name: Boolean => Boolean within fragment - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($booleanArg: Boolean) - { - complicatedArgs { - ...booleanArgFrag - } - } - fragment booleanArgFrag on ComplicatedArgs { - booleanArgField(booleanArg: $booleanArg) - } - - errors: [] -- name: Boolean! => Boolean - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($nonNullBooleanArg: Boolean!) - { - complicatedArgs { - booleanArgField(booleanArg: $nonNullBooleanArg) - } - } - - errors: [] -- name: Boolean! => Boolean within fragment - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - fragment booleanArgFrag on ComplicatedArgs { - booleanArgField(booleanArg: $nonNullBooleanArg) - } - - query Query($nonNullBooleanArg: Boolean!) - { - complicatedArgs { - ...booleanArgFrag - } - } - - errors: [] -- name: '[String] => [String]' - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($stringListVar: [String]) - { - complicatedArgs { - stringListArgField(stringListArg: $stringListVar) - } - } - - errors: [] -- name: '[String!] => [String]' - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($stringListVar: [String!]) - { - complicatedArgs { - stringListArgField(stringListArg: $stringListVar) - } - } - - errors: [] -- name: String => [String] in item position - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($stringVar: String) - { - complicatedArgs { - stringListArgField(stringListArg: [$stringVar]) - } - } - - errors: [] -- name: String! => [String] in item position - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($stringVar: String!) - { - complicatedArgs { - stringListArgField(stringListArg: [$stringVar]) - } - } - - errors: [] -- name: ComplexInput => ComplexInput - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($complexVar: ComplexInput) - { - complicatedArgs { - complexArgField(complexArg: $complexVar) - } - } - - errors: [] -- name: ComplexInput => ComplexInput in field position - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($boolVar: Boolean = false) - { - complicatedArgs { - complexArgField(complexArg: {requiredArg: $boolVar}) - } - } - - errors: [] -- name: Boolean! => Boolean! in directive - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($boolVar: Boolean!) - { - dog @include(if: $boolVar) - } - - errors: [] -- name: Int => Int! - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($intArg: Int) { - complicatedArgs { - nonNullIntArgField(nonNullIntArg: $intArg) - } - } - - errors: - - message: Variable "$intArg" of type "Int" used in position expecting type "Int!". - locations: - - {line: 2, column: 19} - - {line: 4, column: 45} -- name: Int => Int! within fragment - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - fragment nonNullIntArgFieldFrag on ComplicatedArgs { - nonNullIntArgField(nonNullIntArg: $intArg) - } - - query Query($intArg: Int) { - complicatedArgs { - ...nonNullIntArgFieldFrag - } - } - - errors: - - message: Variable "$intArg" of type "Int" used in position expecting type "Int!". - locations: - - {line: 6, column: 19} - - {line: 3, column: 43} -- name: Int => Int! within nested fragment - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - fragment outerFrag on ComplicatedArgs { - ...nonNullIntArgFieldFrag - } - - fragment nonNullIntArgFieldFrag on ComplicatedArgs { - nonNullIntArgField(nonNullIntArg: $intArg) - } - - query Query($intArg: Int) { - complicatedArgs { - ...outerFrag - } - } - - errors: - - message: Variable "$intArg" of type "Int" used in position expecting type "Int!". - locations: - - {line: 10, column: 19} - - {line: 7, column: 43} -- name: String over Boolean - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($stringVar: String) { - complicatedArgs { - booleanArgField(booleanArg: $stringVar) - } - } - - errors: - - message: Variable "$stringVar" of type "String" used in position expecting type "Boolean". - locations: - - {line: 2, column: 19} - - {line: 4, column: 39} -- name: String => [String] - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($stringVar: String) { - complicatedArgs { - stringListArgField(stringListArg: $stringVar) - } - } - - errors: - - message: Variable "$stringVar" of type "String" used in position expecting type "[String]". - locations: - - {line: 2, column: 19} - - {line: 4, column: 45} -- name: Boolean => Boolean! in directive - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($boolVar: Boolean) { - dog @include(if: $boolVar) - } - - errors: - - message: Variable "$boolVar" of type "Boolean" used in position expecting type "Boolean!". - locations: - - {line: 2, column: 19} - - {line: 3, column: 26} -- name: String => Boolean! in directive - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($stringVar: String) { - dog @include(if: $stringVar) - } - - errors: - - message: Variable "$stringVar" of type "String" used in position expecting type "Boolean!". - locations: - - {line: 2, column: 19} - - {line: 3, column: 26} -- name: '[String] => [String!]' - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($stringListVar: [String]) - { - complicatedArgs { - stringListNonNullArgField(stringListNonNullArg: $stringListVar) - } - } - - errors: - - message: Variable "$stringListVar" of type "[String]" used in position expecting type "[String!]". - locations: - - {line: 2, column: 19} - - {line: 5, column: 59} -- name: Allows optional (nullable) variables with default values/Int => Int! fails when variable provides null default value - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($intVar: Int = null) { - complicatedArgs { - nonNullIntArgField(nonNullIntArg: $intVar) - } - } - - errors: - - message: Variable "$intVar" of type "Int" used in position expecting type "Int!". - locations: - - {line: 2, column: 21} - - {line: 4, column: 47} -- name: Allows optional (nullable) variables with default values/Int => Int! when variable provides non-null default value - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($intVar: Int = 1) { - complicatedArgs { - nonNullIntArgField(nonNullIntArg: $intVar) - } - } - errors: [] -- name: Allows optional (nullable) variables with default values/Int => Int! when optional argument provides default value - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($intVar: Int) { - complicatedArgs { - nonNullFieldWithDefault(nonNullIntArg: $intVar) - } - } - errors: [] -- name: Allows optional (nullable) variables with default values/Boolean => Boolean! in directive with default value with option - rule: VariablesInAllowedPosition - schema: 0 - query: |2- - - query Query($boolVar: Boolean = false) { - dog @include(if: $boolVar) - } - errors: [] diff --git a/internal/gqlparser/validator/imported/spec/schemas.yml b/internal/gqlparser/validator/imported/spec/schemas.yml deleted file mode 100644 index a9b62bc456..0000000000 --- a/internal/gqlparser/validator/imported/spec/schemas.yml +++ /dev/null @@ -1,490 +0,0 @@ -- |- - schema { - query: QueryRoot - } - - directive @onField on FIELD - - interface Mammal { - mother: Mammal - father: Mammal - } - - interface Pet { - name(surname: Boolean): String - } - - interface Canine implements Mammal { - name(surname: Boolean): String - mother: Canine - father: Canine - } - - enum DogCommand { - SIT - HEEL - DOWN - } - - type Dog implements Pet & Mammal & Canine { - name(surname: Boolean): String - nickname: String - barkVolume: Int - barks: Boolean - doesKnowCommand(dogCommand: DogCommand): Boolean - isHouseTrained(atOtherHomes: Boolean = true): Boolean - isAtLocation(x: Int, y: Int): Boolean - mother: Dog - father: Dog - } - - type Cat implements Pet { - name(surname: Boolean): String - nickname: String - meows: Boolean - meowsVolume: Int - furColor: FurColor - } - - union CatOrDog = Cat | Dog - - type Human { - name(surname: Boolean): String - pets: [Pet] - relatives: [Human] - } - - enum FurColor { - BROWN - BLACK - TAN - SPOTTED - NO_FUR - UNKNOWN - } - - input ComplexInput { - requiredField: Boolean! - nonNullField: Boolean! = false - intField: Int - stringField: String - booleanField: Boolean - stringListField: [String] - } - - type ComplicatedArgs { - intArgField(intArg: Int): String - nonNullIntArgField(nonNullIntArg: Int!): String - stringArgField(stringArg: String): String - booleanArgField(booleanArg: Boolean): String - enumArgField(enumArg: FurColor): String - floatArgField(floatArg: Float): String - idArgField(idArg: ID): String - stringListArgField(stringListArg: [String]): String - stringListNonNullArgField(stringListNonNullArg: [String!]): String - complexArgField(complexArg: ComplexInput): String - multipleReqs(req1: Int!, req2: Int!): String - nonNullFieldWithDefault(arg: Int! = 0): String - multipleOpts(opt1: Int = 0, opt2: Int = 0): String - multipleOptAndReq(req1: Int!, req2: Int!, opt1: Int = 0, opt2: Int = 0): String - } - - type QueryRoot { - human(id: ID): Human - dog: Dog - cat: Cat - pet: Pet - catOrDog: CatOrDog - complicatedArgs: ComplicatedArgs - } - # injected because upstream spec is missing some types - extend type QueryRoot { - field: T - f1: Type - f2: Type - f3: Type - } - - type Type { - a: String - b: String - c: String - } - type T { - a: String - b: String - c: String - d: String - y: String - deepField: T - deeperField: T - } -- |- - interface Pet { - name: String - } - - type Dog implements Pet { - name: String - nickname: String - barkVolume: Int - } - - type Cat implements Pet { - name: String - nickname: String - meowVolume: Int - } - - union CatOrDog = Cat | Dog - - type Human { - name: String - pets: [Pet] - } - - type Query { - human: Human - } -- |- - directive @onQuery on QUERY - - directive @onMutation on MUTATION - - directive @onSubscription on SUBSCRIPTION - - directive @onField on FIELD - - directive @onFragmentDefinition on FRAGMENT_DEFINITION - - directive @onFragmentSpread on FRAGMENT_SPREAD - - directive @onInlineFragment on INLINE_FRAGMENT - - directive @onVariableDefinition on VARIABLE_DEFINITION - - type Query { - dummy: String - } -- |- - type Query { - foo: String - } -- |- - type Query { - normalField: String - deprecatedField: String @deprecated(reason: "Some field reason.") - } -- |- - type Query { - someField(normalArg: String, deprecatedArg: String @deprecated(reason: "Some arg reason.")): String - } -- |- - directive @someDirective(normalArg: String, deprecatedArg: String @deprecated(reason: "Some arg reason.")) on FIELD - - type Query { - someField: String - } -- |- - directive @someDirective(someArg: InputType) on FIELD - - input InputType { - normalField: String - deprecatedField: String @deprecated(reason: "Some input field reason.") - } - - type Query { - someField(someArg: InputType): String - } -- |- - enum EnumType { - NORMAL_VALUE - DEPRECATED_VALUE @deprecated(reason: "Some enum reason.") - } - - type Query { - someField(enumArg: EnumType): String - } -- |- - type Query { - someQuery: SomeType - } - - type SomeType { - someField: String - introspectionField: __EnumValue - } -- |- - type Query { - someField(a: String, b: String): String - } -- |- - input SomeInput { - a: String - b: String - } - - type Query { - someField(arg: SomeInput): String - } -- |- - interface SomeBox { - deepBox: SomeBox - unrelatedField: String - } - - type StringBox implements SomeBox { - scalar: String - deepBox: StringBox - unrelatedField: String - listStringBox: [StringBox] - stringBox: StringBox - intBox: IntBox - } - - type IntBox implements SomeBox { - scalar: Int - deepBox: IntBox - unrelatedField: String - listStringBox: [StringBox] - stringBox: StringBox - intBox: IntBox - } - - interface NonNullStringBox1 { - scalar: String! - } - - type NonNullStringBox1Impl implements SomeBox & NonNullStringBox1 { - scalar: String! - unrelatedField: String - deepBox: SomeBox - } - - interface NonNullStringBox2 { - scalar: String! - } - - type NonNullStringBox2Impl implements SomeBox & NonNullStringBox2 { - scalar: String! - unrelatedField: String - deepBox: SomeBox - } - - type Connection { - edges: [Edge] - } - - type Edge { - node: Node - } - - type Node { - id: ID - name: String - } - - type Query { - someBox: SomeBox - connection: Connection - } -- |- - type Foo { - constructor: String - } - - type Query { - foo: Foo - } -- |- - interface Being { - name: String - } - - interface Pet implements Being { - name: String - } - - type Dog implements Being & Pet { - name: String - barkVolume: Int - } - - type Cat implements Being & Pet { - name: String - meowVolume: Int - } - - union CatOrDog = Cat | Dog - - interface Intelligent { - iq: Int - } - - type Human implements Being & Intelligent { - name: String - pets: [Pet] - iq: Int - } - - type Alien implements Being & Intelligent { - name: String - iq: Int - } - - union DogOrHuman = Dog | Human - - union HumanOrAlien = Human | Alien - - type Query { - catOrDog: CatOrDog - dogOrHuman: DogOrHuman - humanOrAlien: HumanOrAlien - } -- |- - schema { - query: QueryRoot - subscription: SubscriptionRoot - } - - type Message { - body: String - sender: String - } - - type SubscriptionRoot { - importantEmails: [String] - notImportantEmails: [String] - moreImportantEmails: [String] - spamEmails: [String] - deletedEmails: [String] - newMessage: Message - } - - type QueryRoot { - dummy: String - } -- |- - type Query { - dummy: String - } -- |- - schema { - query: QueryRoot - } - - directive @onField on FIELD - - directive @directive on FIELD | FRAGMENT_DEFINITION - - directive @directiveA on FIELD | FRAGMENT_DEFINITION - - directive @directiveB on FIELD | FRAGMENT_DEFINITION - - directive @repeatable repeatable on FIELD | FRAGMENT_DEFINITION - - interface Mammal { - mother: Mammal - father: Mammal - } - - interface Pet { - name(surname: Boolean): String - } - - interface Canine implements Mammal { - name(surname: Boolean): String - mother: Canine - father: Canine - } - - enum DogCommand { - SIT - HEEL - DOWN - } - - type Dog implements Pet & Mammal & Canine { - name(surname: Boolean): String - nickname: String - barkVolume: Int - barks: Boolean - doesKnowCommand(dogCommand: DogCommand): Boolean - isHouseTrained(atOtherHomes: Boolean = true): Boolean - isAtLocation(x: Int, y: Int): Boolean - mother: Dog - father: Dog - } - - type Cat implements Pet { - name(surname: Boolean): String - nickname: String - meows: Boolean - meowsVolume: Int - furColor: FurColor - } - - union CatOrDog = Cat | Dog - - type Human { - name(surname: Boolean): String - pets: [Pet] - relatives: [Human] - } - - enum FurColor { - BROWN - BLACK - TAN - SPOTTED - NO_FUR - UNKNOWN - } - - input ComplexInput { - requiredField: Boolean! - nonNullField: Boolean! = false - intField: Int - stringField: String - booleanField: Boolean - stringListField: [String] - } - - type ComplicatedArgs { - intArgField(intArg: Int): String - nonNullIntArgField(nonNullIntArg: Int!): String - stringArgField(stringArg: String): String - booleanArgField(booleanArg: Boolean): String - enumArgField(enumArg: FurColor): String - floatArgField(floatArg: Float): String - idArgField(idArg: ID): String - stringListArgField(stringListArg: [String]): String - stringListNonNullArgField(stringListNonNullArg: [String!]): String - complexArgField(complexArg: ComplexInput): String - multipleReqs(req1: Int!, req2: Int!): String - nonNullFieldWithDefault(arg: Int! = 0): String - multipleOpts(opt1: Int = 0, opt2: Int = 0): String - multipleOptAndReq(req1: Int!, req2: Int!, opt1: Int = 0, opt2: Int = 0): String - } - - type QueryRoot { - human(id: ID): Human - dog: Dog - cat: Cat - pet: Pet - catOrDog: CatOrDog - complicatedArgs: ComplicatedArgs - } -- |- - type Query { - invalidArg(arg: CustomScalar): String - } - - scalar CustomScalar -- |- - type Query { - anyArg(arg: Any): String - } - - scalar Any -- "" diff --git a/internal/gqlparser/validator/prelude.graphql b/internal/gqlparser/validator/prelude.graphql deleted file mode 100644 index bdca0096a5..0000000000 --- a/internal/gqlparser/validator/prelude.graphql +++ /dev/null @@ -1,121 +0,0 @@ -# This file defines all the implicitly declared types that are required by the graphql spec. It is implicitly included by calls to LoadSchema - -"The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1." -scalar Int - -"The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)." -scalar Float - -"The `String`scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text." -scalar String - -"The `Boolean` scalar type represents `true` or `false`." -scalar Boolean - -"""The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID.""" -scalar ID - -"The @include directive may be provided for fields, fragment spreads, and inline fragments, and allows for conditional inclusion during execution as described by the if argument." -directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - -"The @skip directive may be provided for fields, fragment spreads, and inline fragments, and allows for conditional exclusion during execution as described by the if argument." -directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - -"The @deprecated built-in directive is used within the type system definition language to indicate deprecated portions of a GraphQL service's schema, such as deprecated fields on a type, arguments on a field, input fields on an input type, or values of an enum type." -directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE - -"The @specifiedBy built-in directive is used within the type system definition language to provide a scalar specification URL for specifying the behavior of custom scalar types." -directive @specifiedBy(url: String!) on SCALAR - -type __Schema { - description: String - types: [__Type!]! - queryType: __Type! - mutationType: __Type - subscriptionType: __Type - directives: [__Directive!]! -} - -type __Type { - kind: __TypeKind! - name: String - description: String - # must be non-null for OBJECT and INTERFACE, otherwise null. - fields(includeDeprecated: Boolean = false): [__Field!] - # must be non-null for OBJECT and INTERFACE, otherwise null. - interfaces: [__Type!] - # must be non-null for INTERFACE and UNION, otherwise null. - possibleTypes: [__Type!] - # must be non-null for ENUM, otherwise null. - enumValues(includeDeprecated: Boolean = false): [__EnumValue!] - # must be non-null for INPUT_OBJECT, otherwise null. - inputFields: [__InputValue!] - # must be non-null for NON_NULL and LIST, otherwise null. - ofType: __Type - # may be non-null for custom SCALAR, otherwise null. - specifiedByURL: String -} - -type __Field { - name: String! - description: String - args: [__InputValue!]! - type: __Type! - isDeprecated: Boolean! - deprecationReason: String -} - -type __InputValue { - name: String! - description: String - type: __Type! - defaultValue: String -} - -type __EnumValue { - name: String! - description: String - isDeprecated: Boolean! - deprecationReason: String -} - -enum __TypeKind { - SCALAR - OBJECT - INTERFACE - UNION - ENUM - INPUT_OBJECT - LIST - NON_NULL -} - -type __Directive { - name: String! - description: String - locations: [__DirectiveLocation!]! - args: [__InputValue!]! - isRepeatable: Boolean! -} - -enum __DirectiveLocation { - QUERY - MUTATION - SUBSCRIPTION - FIELD - FRAGMENT_DEFINITION - FRAGMENT_SPREAD - INLINE_FRAGMENT - VARIABLE_DEFINITION - SCHEMA - SCALAR - OBJECT - FIELD_DEFINITION - ARGUMENT_DEFINITION - INTERFACE - UNION - ENUM - ENUM_VALUE - INPUT_OBJECT - INPUT_FIELD_DEFINITION -} diff --git a/internal/gqlparser/validator/rules/known_argument_names.go b/internal/gqlparser/validator/rules/known_argument_names.go deleted file mode 100644 index 36b2d057c9..0000000000 --- a/internal/gqlparser/validator/rules/known_argument_names.go +++ /dev/null @@ -1,59 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("KnownArgumentNames", func(observers *Events, addError AddErrFunc) { - // A GraphQL field is only valid if all supplied arguments are defined by that field. - observers.OnField(func(_ *Walker, field *ast.Field) { - if field.Definition == nil || field.ObjectDefinition == nil { - return - } - for _, arg := range field.Arguments { - def := field.Definition.Arguments.ForName(arg.Name) - if def != nil { - continue - } - - var suggestions []string - for _, argDef := range field.Definition.Arguments { - suggestions = append(suggestions, argDef.Name) - } - - addError( - Message(`Unknown argument "%s" on field "%s.%s".`, arg.Name, field.ObjectDefinition.Name, field.Name), - SuggestListQuoted("Did you mean", arg.Name, suggestions), - At(field.Position), - ) - } - }) - - observers.OnDirective(func(_ *Walker, directive *ast.Directive) { - if directive.Definition == nil { - return - } - for _, arg := range directive.Arguments { - def := directive.Definition.Arguments.ForName(arg.Name) - if def != nil { - continue - } - - var suggestions []string - for _, argDef := range directive.Definition.Arguments { - suggestions = append(suggestions, argDef.Name) - } - - addError( - Message(`Unknown argument "%s" on directive "@%s".`, arg.Name, directive.Name), - SuggestListQuoted("Did you mean", arg.Name, suggestions), - At(directive.Position), - ) - } - }) - }) -} diff --git a/internal/gqlparser/validator/rules/known_fragment_names.go b/internal/gqlparser/validator/rules/known_fragment_names.go deleted file mode 100644 index 8ae1fc33f4..0000000000 --- a/internal/gqlparser/validator/rules/known_fragment_names.go +++ /dev/null @@ -1,21 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("KnownFragmentNames", func(observers *Events, addError AddErrFunc) { - observers.OnFragmentSpread(func(_ *Walker, fragmentSpread *ast.FragmentSpread) { - if fragmentSpread.Definition == nil { - addError( - Message(`Unknown fragment "%s".`, fragmentSpread.Name), - At(fragmentSpread.Position), - ) - } - }) - }) -} diff --git a/internal/gqlparser/validator/rules/known_type_names.go b/internal/gqlparser/validator/rules/known_type_names.go deleted file mode 100644 index aa9809be34..0000000000 --- a/internal/gqlparser/validator/rules/known_type_names.go +++ /dev/null @@ -1,61 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("KnownTypeNames", func(observers *Events, addError AddErrFunc) { - observers.OnVariable(func(walker *Walker, variable *ast.VariableDefinition) { - typeName := variable.Type.Name() - typdef := walker.Schema.Types[typeName] - if typdef != nil { - return - } - - addError( - Message(`Unknown type "%s".`, typeName), - At(variable.Position), - ) - }) - - observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) { - typedName := inlineFragment.TypeCondition - if typedName == "" { - return - } - - def := walker.Schema.Types[typedName] - if def != nil { - return - } - - addError( - Message(`Unknown type "%s".`, typedName), - At(inlineFragment.Position), - ) - }) - - observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { - typeName := fragment.TypeCondition - def := walker.Schema.Types[typeName] - if def != nil { - return - } - - var possibleTypes []string - for _, t := range walker.Schema.Types { - possibleTypes = append(possibleTypes, t.Name) - } - - addError( - Message(`Unknown type "%s".`, typeName), - SuggestListQuoted("Did you mean", typeName, possibleTypes), - At(fragment.Position), - ) - }) - }) -} diff --git a/internal/gqlparser/validator/rules/lone_anonymous_operation.go b/internal/gqlparser/validator/rules/lone_anonymous_operation.go deleted file mode 100644 index 2af7b5a038..0000000000 --- a/internal/gqlparser/validator/rules/lone_anonymous_operation.go +++ /dev/null @@ -1,21 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("LoneAnonymousOperation", func(observers *Events, addError AddErrFunc) { - observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { - if operation.Name == "" && len(walker.Document.Operations) > 1 { - addError( - Message(`This anonymous operation must be the only defined operation.`), - At(operation.Position), - ) - } - }) - }) -} diff --git a/internal/gqlparser/validator/rules/no_unused_fragments.go b/internal/gqlparser/validator/rules/no_unused_fragments.go deleted file mode 100644 index f6ba046a1c..0000000000 --- a/internal/gqlparser/validator/rules/no_unused_fragments.go +++ /dev/null @@ -1,32 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("NoUnusedFragments", func(observers *Events, addError AddErrFunc) { - - inFragmentDefinition := false - fragmentNameUsed := make(map[string]bool) - - observers.OnFragmentSpread(func(_ *Walker, fragmentSpread *ast.FragmentSpread) { - if !inFragmentDefinition { - fragmentNameUsed[fragmentSpread.Name] = true - } - }) - - observers.OnFragment(func(_ *Walker, fragment *ast.FragmentDefinition) { - inFragmentDefinition = true - if !fragmentNameUsed[fragment.Name] { - addError( - Message(`Fragment "%s" is never used.`, fragment.Name), - At(fragment.Position), - ) - } - }) - }) -} diff --git a/internal/gqlparser/validator/rules/no_unused_variables.go b/internal/gqlparser/validator/rules/no_unused_variables.go deleted file mode 100644 index 163ac895b5..0000000000 --- a/internal/gqlparser/validator/rules/no_unused_variables.go +++ /dev/null @@ -1,32 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("NoUnusedVariables", func(observers *Events, addError AddErrFunc) { - observers.OnOperation(func(_ *Walker, operation *ast.OperationDefinition) { - for _, varDef := range operation.VariableDefinitions { - if varDef.Used { - continue - } - - if operation.Name != "" { - addError( - Message(`Variable "$%s" is never used in operation "%s".`, varDef.Variable, operation.Name), - At(varDef.Position), - ) - } else { - addError( - Message(`Variable "$%s" is never used.`, varDef.Variable), - At(varDef.Position), - ) - } - } - }) - }) -} diff --git a/internal/gqlparser/validator/rules/unique_argument_names.go b/internal/gqlparser/validator/rules/unique_argument_names.go deleted file mode 100644 index 7458c5f6cb..0000000000 --- a/internal/gqlparser/validator/rules/unique_argument_names.go +++ /dev/null @@ -1,35 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("UniqueArgumentNames", func(observers *Events, addError AddErrFunc) { - observers.OnField(func(_ *Walker, field *ast.Field) { - checkUniqueArgs(field.Arguments, addError) - }) - - observers.OnDirective(func(_ *Walker, directive *ast.Directive) { - checkUniqueArgs(directive.Arguments, addError) - }) - }) -} - -func checkUniqueArgs(args ast.ArgumentList, addError AddErrFunc) { - knownArgNames := map[string]int{} - - for _, arg := range args { - if knownArgNames[arg.Name] == 1 { - addError( - Message(`There can be only one argument named "%s".`, arg.Name), - At(arg.Position), - ) - } - - knownArgNames[arg.Name]++ - } -} diff --git a/internal/gqlparser/validator/rules/unique_directives_per_location.go b/internal/gqlparser/validator/rules/unique_directives_per_location.go deleted file mode 100644 index ecf5a0a82e..0000000000 --- a/internal/gqlparser/validator/rules/unique_directives_per_location.go +++ /dev/null @@ -1,26 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("UniqueDirectivesPerLocation", func(observers *Events, addError AddErrFunc) { - observers.OnDirectiveList(func(_ *Walker, directives []*ast.Directive) { - seen := map[string]bool{} - - for _, dir := range directives { - if dir.Name != "repeatable" && seen[dir.Name] { - addError( - Message(`The directive "@%s" can only be used once at this location.`, dir.Name), - At(dir.Position), - ) - } - seen[dir.Name] = true - } - }) - }) -} diff --git a/internal/gqlparser/validator/rules/unique_fragment_names.go b/internal/gqlparser/validator/rules/unique_fragment_names.go deleted file mode 100644 index c94f3ad27c..0000000000 --- a/internal/gqlparser/validator/rules/unique_fragment_names.go +++ /dev/null @@ -1,24 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("UniqueFragmentNames", func(observers *Events, addError AddErrFunc) { - seenFragments := map[string]bool{} - - observers.OnFragment(func(_ *Walker, fragment *ast.FragmentDefinition) { - if seenFragments[fragment.Name] { - addError( - Message(`There can be only one fragment named "%s".`, fragment.Name), - At(fragment.Position), - ) - } - seenFragments[fragment.Name] = true - }) - }) -} diff --git a/internal/gqlparser/validator/rules/unique_input_field_names.go b/internal/gqlparser/validator/rules/unique_input_field_names.go deleted file mode 100644 index a93d63bd1e..0000000000 --- a/internal/gqlparser/validator/rules/unique_input_field_names.go +++ /dev/null @@ -1,29 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("UniqueInputFieldNames", func(observers *Events, addError AddErrFunc) { - observers.OnValue(func(_ *Walker, value *ast.Value) { - if value.Kind != ast.ObjectValue { - return - } - - seen := map[string]bool{} - for _, field := range value.Children { - if seen[field.Name] { - addError( - Message(`There can be only one input field named "%s".`, field.Name), - At(field.Position), - ) - } - seen[field.Name] = true - } - }) - }) -} diff --git a/internal/gqlparser/validator/rules/unique_operation_names.go b/internal/gqlparser/validator/rules/unique_operation_names.go deleted file mode 100644 index dcd404dadf..0000000000 --- a/internal/gqlparser/validator/rules/unique_operation_names.go +++ /dev/null @@ -1,24 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("UniqueOperationNames", func(observers *Events, addError AddErrFunc) { - seen := map[string]bool{} - - observers.OnOperation(func(_ *Walker, operation *ast.OperationDefinition) { - if seen[operation.Name] { - addError( - Message(`There can be only one operation named "%s".`, operation.Name), - At(operation.Position), - ) - } - seen[operation.Name] = true - }) - }) -} diff --git a/internal/gqlparser/validator/rules/unique_variable_names.go b/internal/gqlparser/validator/rules/unique_variable_names.go deleted file mode 100644 index 7a214dbe4c..0000000000 --- a/internal/gqlparser/validator/rules/unique_variable_names.go +++ /dev/null @@ -1,26 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("UniqueVariableNames", func(observers *Events, addError AddErrFunc) { - observers.OnOperation(func(_ *Walker, operation *ast.OperationDefinition) { - seen := map[string]int{} - for _, def := range operation.VariableDefinitions { - // add the same error only once per a variable. - if seen[def.Variable] == 1 { - addError( - Message(`There can be only one variable named "$%s".`, def.Variable), - At(def.Position), - ) - } - seen[def.Variable]++ - } - }) - }) -} diff --git a/internal/gqlparser/validator/rules/values_of_correct_type.go b/internal/gqlparser/validator/rules/values_of_correct_type.go deleted file mode 100644 index afd9f54f10..0000000000 --- a/internal/gqlparser/validator/rules/values_of_correct_type.go +++ /dev/null @@ -1,168 +0,0 @@ -package validator - -import ( - "errors" - "fmt" - "strconv" - - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("ValuesOfCorrectType", func(observers *Events, addError AddErrFunc) { - observers.OnValue(func(_ *Walker, value *ast.Value) { - if value.Definition == nil || value.ExpectedType == nil { - return - } - - if value.Kind == ast.NullValue && value.ExpectedType.NonNull { - addError( - Message(`Expected value of type "%s", found %s.`, value.ExpectedType.String(), value.String()), - At(value.Position), - ) - } - - if value.Definition.Kind == ast.Scalar { - // Skip custom validating scalars - if !value.Definition.OneOf("Int", "Float", "String", "Boolean", "ID") { - return - } - } - - var possibleEnums []string - if value.Definition.Kind == ast.Enum { - for _, val := range value.Definition.EnumValues { - possibleEnums = append(possibleEnums, val.Name) - } - } - - rawVal, err := value.Value(nil) - if err != nil { - unexpectedTypeMessage(addError, value) - } - - switch value.Kind { - case ast.NullValue: - return - case ast.ListValue: - if value.ExpectedType.Elem == nil { - unexpectedTypeMessage(addError, value) - return - } - - case ast.IntValue: - if !value.Definition.OneOf("Int", "Float", "ID") { - unexpectedTypeMessage(addError, value) - } - - case ast.FloatValue: - if !value.Definition.OneOf("Float") { - unexpectedTypeMessage(addError, value) - } - - case ast.StringValue, ast.BlockValue: - if value.Definition.Kind == ast.Enum { - rawValStr := fmt.Sprint(rawVal) - addError( - Message(`Enum "%s" cannot represent non-enum value: %s.`, value.ExpectedType.String(), value.String()), - SuggestListQuoted("Did you mean the enum value", rawValStr, possibleEnums), - At(value.Position), - ) - } else if !value.Definition.OneOf("String", "ID") { - unexpectedTypeMessage(addError, value) - } - - case ast.EnumValue: - if value.Definition.Kind != ast.Enum { - rawValStr := fmt.Sprint(rawVal) - addError( - unexpectedTypeMessageOnly(value), - SuggestListUnquoted("Did you mean the enum value", rawValStr, possibleEnums), - At(value.Position), - ) - } else if value.Definition.EnumValues.ForName(value.Raw) == nil { - rawValStr := fmt.Sprint(rawVal) - addError( - Message(`Value "%s" does not exist in "%s" enum.`, value.String(), value.ExpectedType.String()), - SuggestListQuoted("Did you mean the enum value", rawValStr, possibleEnums), - At(value.Position), - ) - } - - case ast.BooleanValue: - if !value.Definition.OneOf("Boolean") { - unexpectedTypeMessage(addError, value) - } - - case ast.ObjectValue: - - for _, field := range value.Definition.Fields { - if field.Type.NonNull { - fieldValue := value.Children.ForName(field.Name) - if fieldValue == nil && field.DefaultValue == nil { - addError( - Message(`Field "%s.%s" of required type "%s" was not provided.`, value.Definition.Name, field.Name, field.Type.String()), - At(value.Position), - ) - continue - } - } - } - - for _, fieldValue := range value.Children { - if value.Definition.Fields.ForName(fieldValue.Name) == nil { - var suggestions []string - for _, fieldValue := range value.Definition.Fields { - suggestions = append(suggestions, fieldValue.Name) - } - - addError( - Message(`Field "%s" is not defined by type "%s".`, fieldValue.Name, value.Definition.Name), - SuggestListQuoted("Did you mean", fieldValue.Name, suggestions), - At(fieldValue.Position), - ) - } - } - - case ast.Variable: - return - - default: - panic(fmt.Errorf("unhandled %T", value)) - } - }) - }) -} - -func unexpectedTypeMessage(addError AddErrFunc, v *ast.Value) { - addError( - unexpectedTypeMessageOnly(v), - At(v.Position), - ) -} - -func unexpectedTypeMessageOnly(v *ast.Value) ErrorOption { - switch v.ExpectedType.String() { - case "Int", "Int!": - if _, err := strconv.ParseInt(v.Raw, 10, 32); err != nil && errors.Is(err, strconv.ErrRange) { - return Message(`Int cannot represent non 32-bit signed integer value: %s`, v.String()) - } - return Message(`Int cannot represent non-integer value: %s`, v.String()) - case "String", "String!", "[String]": - return Message(`String cannot represent a non string value: %s`, v.String()) - case "Boolean", "Boolean!": - return Message(`Boolean cannot represent a non boolean value: %s`, v.String()) - case "Float", "Float!": - return Message(`Float cannot represent non numeric value: %s`, v.String()) - case "ID", "ID!": - return Message(`ID cannot represent a non-string and non-integer value: %s`, v.String()) - default: - if v.Definition.Kind == ast.Enum { - return Message(`Enum "%s" cannot represent non-enum value: %s.`, v.ExpectedType.String(), v.String()) - } - return Message(`Expected value of type "%s", found %s.`, v.ExpectedType.String(), v.String()) - } -} diff --git a/internal/gqlparser/validator/rules/variables_are_input_types.go b/internal/gqlparser/validator/rules/variables_are_input_types.go deleted file mode 100644 index ea4dfcc5ab..0000000000 --- a/internal/gqlparser/validator/rules/variables_are_input_types.go +++ /dev/null @@ -1,30 +0,0 @@ -package validator - -import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" -) - -func init() { - AddRule("VariablesAreInputTypes", func(observers *Events, addError AddErrFunc) { - observers.OnOperation(func(_ *Walker, operation *ast.OperationDefinition) { - for _, def := range operation.VariableDefinitions { - if def.Definition == nil { - continue - } - if !def.Definition.IsInputType() { - addError( - Message( - `Variable "$%s" cannot be non-input type "%s".`, - def.Variable, - def.Type.String(), - ), - At(def.Position), - ) - } - } - }) - }) -} diff --git a/internal/gqlparser/validator/spec/FragmentsOnCompositeTypes.spec.yml b/internal/gqlparser/validator/spec/FragmentsOnCompositeTypes.spec.yml deleted file mode 100644 index ec8ad0be34..0000000000 --- a/internal/gqlparser/validator/spec/FragmentsOnCompositeTypes.spec.yml +++ /dev/null @@ -1,9 +0,0 @@ -- name: Undefined type assertions - schema: 0 - query: | - fragment c on Float - {...c} - - errors: - - message: Fragment "c" cannot condition on non composite type "Float". - - message: Cannot spread fragment "c" within itself. diff --git a/internal/gqlparser/validator/spec/Fuzz.spec.yml b/internal/gqlparser/validator/spec/Fuzz.spec.yml deleted file mode 100644 index f48cacf453..0000000000 --- a/internal/gqlparser/validator/spec/Fuzz.spec.yml +++ /dev/null @@ -1,34 +0,0 @@ -- name: 01 - schema: 0 - query: '{r{__typename(s:0)}}' - errors: - - message: Cannot query field "r" on type "QueryRoot". - -- name: 02 - infinite loop occurred in OverlappingFieldsCanBeMerged rule - schema: | - type Query { - fieldA: A - } - type A { - s: String - } - query: | - { ...F } - fragment F on Query { - fieldA { s } - ...{ - ...{ - ...F - } - fieldA { - ...notExists - } - } - } - errors: - # from KnownFragmentNames rule - - message: Unknown fragment "notExists". - - message: Unknown fragment "notExists". - - message: Unknown fragment "notExists". - # from NoFragmentCycles rule - - message: Cannot spread fragment "F" within itself. diff --git a/internal/gqlparser/validator/spec/KnownRootTypeRule.spec.yml b/internal/gqlparser/validator/spec/KnownRootTypeRule.spec.yml deleted file mode 100644 index 651e6582b1..0000000000 --- a/internal/gqlparser/validator/spec/KnownRootTypeRule.spec.yml +++ /dev/null @@ -1,19 +0,0 @@ -- name: Known root type - rule: KnownRootType - schema: 0 - query: | - query { dog { name } } -- name: Valid root type but not in schema - rule: KnownRootType - schema: 0 - query: | - mutation { dog { name } } - errors: - - message: Schema does not support operation type "mutation" -- name: Valid root type but schema is entirely empty - rule: KnownRootType - schema: 20 - query: | - { dog { name } } - errors: - - message: Schema does not support operation type "query" diff --git a/internal/gqlparser/validator/spec/NonExistantTypes.spec.yml b/internal/gqlparser/validator/spec/NonExistantTypes.spec.yml deleted file mode 100644 index a9778b6195..0000000000 --- a/internal/gqlparser/validator/spec/NonExistantTypes.spec.yml +++ /dev/null @@ -1,20 +0,0 @@ -- name: Undefined type assertions - schema: 0 - query: | - query panic{ - panic{ - ...PanicInput - __typename - } - } - fragment PanicInput on Panic { - __typename - } - - errors: - - message: Unknown type "Panic". Did you mean "Canine"? - locations: - - {line: 7, column: 7} - - message: Cannot query field "panic" on type "QueryRoot". - locations: - - {line: 2, column: 14} diff --git a/internal/gqlparser/validator/testdata/default_root_operation_type_names.graphql b/internal/gqlparser/validator/testdata/default_root_operation_type_names.graphql deleted file mode 100644 index 1e4cf6c8fc..0000000000 --- a/internal/gqlparser/validator/testdata/default_root_operation_type_names.graphql +++ /dev/null @@ -1,16 +0,0 @@ -schema { - query: Query -} - -type Query { - mutation: Mutation! - subscription: Subscription! -} - -type Mutation { - name: String! -} - -type Subscription { - name: String! -} diff --git a/internal/gqlparser/validator/testdata/extensions.graphql b/internal/gqlparser/validator/testdata/extensions.graphql deleted file mode 100644 index ab7537039b..0000000000 --- a/internal/gqlparser/validator/testdata/extensions.graphql +++ /dev/null @@ -1,31 +0,0 @@ -schema { - query: Query -} - -extend schema { - subscription: Subscription -} - -type Query { - dogs: [Dog!]! -} - -type Subscription { - dogEvents: [Dog!]! -} - -type Dog { - name: String! -} - -type Person @favorite(name: "sushi") @favorite(name: "tempura") { - name: String! -} - -directive @favorite(name: String!) repeatable on OBJECT - -extend type Dog { - owner: Person! @permission(permission: "admin") -} - -directive @permission(permission: String!) on FIELD_DEFINITION diff --git a/internal/gqlparser/validator/testdata/interfaces.graphql b/internal/gqlparser/validator/testdata/interfaces.graphql deleted file mode 100644 index 1555188b55..0000000000 --- a/internal/gqlparser/validator/testdata/interfaces.graphql +++ /dev/null @@ -1,14 +0,0 @@ -interface Mammal { - mother: Mammal - father: Mammal -} - -interface Pet { - name(surname: Boolean): String -} - -interface Canine implements Mammal { - name(surname: Boolean): String - mother: Canine - father: Canine -} \ No newline at end of file diff --git a/internal/gqlparser/validator/testdata/swapi.graphql b/internal/gqlparser/validator/testdata/swapi.graphql deleted file mode 100644 index 38422b7f71..0000000000 --- a/internal/gqlparser/validator/testdata/swapi.graphql +++ /dev/null @@ -1,147 +0,0 @@ -schema { - query: Query - mutation: Mutation - subscription: Subscription -} - -# The query type, represents all of the entry points into our object graph -type Query { - hero(episode: Episode): Character - reviews(episode: Episode!): [Review] - search(text: String): [SearchResult] - character(id: ID!): Character - droid(id: ID!): Droid - human(id: ID!): Human - starship(id: ID!): Starship -} -# The mutation type, represents all updates we can make to our data -type Mutation { - createReview(episode: Episode, review: ReviewInput!): Review -} -# The subscription type, represents all subscriptions we can make to our data -type Subscription { - reviewAdded(episode: Episode): Review -} -# The episodes in the Star Wars trilogy -enum Episode { - # Star Wars Episode IV: A New Hope, released in 1977. - NEWHOPE - # Star Wars Episode V: The Empire Strikes Back, released in 1980. - EMPIRE - # Star Wars Episode VI: Return of the Jedi, released in 1983. - JEDI -} -# A character from the Star Wars universe -interface Character { - # The ID of the character - id: ID! - # The name of the character - name: String! - # The friends of the character, or an empty list if they have none - friends: [Character] - # The friends of the character exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this character appears in - appearsIn: [Episode]! -} -# Units of height -enum LengthUnit { - # The standard unit around the world - METER - # Primarily used in the United States - FOOT -} -# A humanoid creature from the Star Wars universe -type Human implements Character { - # The ID of the human - id: ID! - # What this human calls themselves - name: String! - # The home planet of the human, or null if unknown - homePlanet: String - # Height in the preferred unit, default is meters - height(unit: LengthUnit = METER): Float - # Mass in kilograms, or null if unknown - mass: Float - # This human's friends, or an empty list if they have none - friends: [Character] - # The friends of the human exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this human appears in - appearsIn: [Episode]! - # A list of starships this person has piloted, or an empty list if none - starships: [Starship] -} -# An autonomous mechanical character in the Star Wars universe -type Droid implements Character { - # The ID of the droid - id: ID! - # What others call this droid - name: String! - # This droid's friends, or an empty list if they have none - friends: [Character] - # The friends of the droid exposed as a connection with edges - friendsConnection(first: Int, after: ID): FriendsConnection! - # The movies this droid appears in - appearsIn: [Episode]! - # This droid's primary function - primaryFunction: String -} -# A connection object for a character's friends -type FriendsConnection { - # The total number of friends - totalCount: Int - # The edges for each of the character's friends. - edges: [FriendsEdge] - # A list of the friends, as a convenience when edges are not needed. - friends: [Character] - # Information for paginating this connection - pageInfo: PageInfo! -} -# An edge object for a character's friends -type FriendsEdge { - # A cursor used for pagination - cursor: ID! - # The character represented by this friendship edge - node: Character -} -# Information for paginating this connection -type PageInfo { - startCursor: ID - endCursor: ID - hasNextPage: Boolean! -} -# Represents a review for a movie -type Review { - # The movie - episode: Episode - # The number of stars this review gave, 1-5 - stars: Int! - # Comment about the movie - commentary: String -} -# The input object sent when someone is creating a new review -input ReviewInput { - # 0-5 stars - stars: Int! - # Comment about the movie, optional - commentary: String - # Favorite color, optional - favorite_color: ColorInput -} -# The input object sent when passing in a color -input ColorInput { - red: Int! - green: Int! - blue: Int! -} -type Starship { - # The ID of the starship - id: ID! - # The name of the starship - name: String! - # Length of the starship, along the longest axis - length(unit: LengthUnit = METER): Float - coordinates: [[Float!]!] -} -union SearchResult = Human | Droid | Starship \ No newline at end of file diff --git a/internal/gqlparser/validator/testdata/vars.graphql b/internal/gqlparser/validator/testdata/vars.graphql deleted file mode 100644 index fe2e6cfd6d..0000000000 --- a/internal/gqlparser/validator/testdata/vars.graphql +++ /dev/null @@ -1,38 +0,0 @@ -type Query { - optionalIntArg(i: Int): Boolean! - intArg(i: Int!): Boolean! - stringArg(i: String): Boolean! - boolArg(i: Boolean!): Boolean! - floatArg(i: Float!): Boolean! - idArg(i: ID!): Boolean! - scalarArg(i: Custom!): Boolean! - structArg(i: InputType!): Boolean! - defaultStructArg(i: InputType! = {name: "foo"}): Boolean! - arrayArg(i: [InputType!]): Boolean! - intArrayArg(i: [Int]): Boolean! - stringArrayArg(i: [String]): Boolean! - boolArrayArg(i: [Boolean]): Boolean! - typeArrayArg(i: [CustomType]): Boolean! -} - -input InputType { - name: String! - nullName: String - nullEmbedded: Embedded - enum: Enum - defaultName: String! = "defaultFoo" -} - -input Embedded { - name: String! -} - -input CustomType { - and: [Int!] -} - -enum Enum { - A -} - -scalar Custom diff --git a/internal/gqlparser/validator/validator.go b/internal/gqlparser/validator/validator.go deleted file mode 100644 index 05f5b91669..0000000000 --- a/internal/gqlparser/validator/validator.go +++ /dev/null @@ -1,45 +0,0 @@ -package validator - -import ( - //nolint:revive - . "github.com/open-policy-agent/opa/internal/gqlparser/ast" - "github.com/open-policy-agent/opa/internal/gqlparser/gqlerror" -) - -type AddErrFunc func(options ...ErrorOption) - -type ruleFunc func(observers *Events, addError AddErrFunc) - -type rule struct { - name string - rule ruleFunc -} - -var rules []rule - -// addRule to rule set. -// f is called once each time `Validate` is executed. -func AddRule(name string, f ruleFunc) { - rules = append(rules, rule{name: name, rule: f}) -} - -func Validate(schema *Schema, doc *QueryDocument) gqlerror.List { - var errs gqlerror.List - - observers := &Events{} - for i := range rules { - rule := rules[i] - rule.rule(observers, func(options ...ErrorOption) { - err := &gqlerror.Error{ - Rule: rule.name, - } - for _, o := range options { - o(err) - } - errs = append(errs, err) - }) - } - - Walk(schema, doc, observers) - return errs -} diff --git a/topdown/graphql.go b/topdown/graphql.go index d08e2fb692..6b52705620 100644 --- a/topdown/graphql.go +++ b/topdown/graphql.go @@ -9,12 +9,12 @@ import ( "fmt" "strings" - gqlast "github.com/open-policy-agent/opa/internal/gqlparser/ast" - gqlparser "github.com/open-policy-agent/opa/internal/gqlparser/parser" - gqlvalidator "github.com/open-policy-agent/opa/internal/gqlparser/validator" + gqlast "github.com/vektah/gqlparser/v2/ast" + gqlparser "github.com/vektah/gqlparser/v2/parser" + gqlvalidator "github.com/vektah/gqlparser/v2/validator" // Side-effecting import. Triggers GraphQL library's validation rule init() functions. - _ "github.com/open-policy-agent/opa/internal/gqlparser/validator/rules" + _ "github.com/vektah/gqlparser/v2/validator/rules" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/topdown/builtins" diff --git a/v1/test/cases/testdata/v0/graphql/test-graphql-parse-and-verify.yaml b/v1/test/cases/testdata/v0/graphql/test-graphql-parse-and-verify.yaml index af41c8b2ff..5918c6b4ef 100644 --- a/v1/test/cases/testdata/v0/graphql/test-graphql-parse-and-verify.yaml +++ b/v1/test/cases/testdata/v0/graphql/test-graphql-parse-and-verify.yaml @@ -76,6 +76,9 @@ cases: - | package test schema := ` + type Query { + x: Int + } extend type Query { myAction(myEnum: Locale!): SomeResult! } @@ -89,7 +92,6 @@ cases: } ` query := ` - # Note: there is default enum value in variables query SomeOperation ($locale: Locale! = DE) { myAction(myEnum: $locale) { id diff --git a/v1/test/cases/testdata/v0/graphql/test-graphql-parse-query.yaml b/v1/test/cases/testdata/v0/graphql/test-graphql-parse-query.yaml index 9ef7ca081b..6c25e0aa7d 100644 --- a/v1/test/cases/testdata/v0/graphql/test-graphql-parse-query.yaml +++ b/v1/test/cases/testdata/v0/graphql/test-graphql-parse-query.yaml @@ -388,11 +388,11 @@ cases: modules: - | package test + # Copyright (c) 2015-present, Facebook, Inc. + # + # This source code is licensed under the MIT license found in the + # LICENSE file in the root directory of this source tree. gql := ` - # Copyright (c) 2015-present, Facebook, Inc. - # - # This source code is licensed under the MIT license found in the - # LICENSE file in the root directory of this source tree. query queryName($foo: ComplexType, $site: Site = MOBILE) { whoever123is: node(id: [123, 456]) { id , diff --git a/v1/test/cases/testdata/v0/graphql/test-graphql-parse-schema.yaml b/v1/test/cases/testdata/v0/graphql/test-graphql-parse-schema.yaml index 0c95403fb9..07ae43f368 100644 --- a/v1/test/cases/testdata/v0/graphql/test-graphql-parse-schema.yaml +++ b/v1/test/cases/testdata/v0/graphql/test-graphql-parse-schema.yaml @@ -48,12 +48,14 @@ cases: """ Description """ - # Even with comments between them type Hello { world: String } + type Query { + hello: Hello + } ` - ast := {"Definitions": [{"BuiltIn": false, "Description": "Description", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + ast := {"Definitions":[{"BuiltIn":false,"Description":"Description","Fields":[{"Description":"","Name":"world","Type":{"NamedType":"String","NonNull":false}}],"Kind":"OBJECT","Name":"Hello"},{"BuiltIn":false,"Description":"","Fields":[{"Description":"","Name":"hello","Type":{"NamedType":"Hello","NonNull":false}}],"Kind":"OBJECT","Name":"Query"}]} p { graphql.parse_schema(gql) == ast } diff --git a/v1/test/cases/testdata/v0/graphql/test-graphql-parse.yaml b/v1/test/cases/testdata/v0/graphql/test-graphql-parse.yaml index ff0c65ca4c..ea00be2034 100644 --- a/v1/test/cases/testdata/v0/graphql/test-graphql-parse.yaml +++ b/v1/test/cases/testdata/v0/graphql/test-graphql-parse.yaml @@ -75,6 +75,9 @@ cases: - | package test schema := ` + type Query { + x: Int + } extend type Query { myAction(myEnum: Locale!): SomeResult! } @@ -88,7 +91,6 @@ cases: } ` query := ` - # Note: there is default enum value in variables query SomeOperation ($locale: Locale! = DE) { myAction(myEnum: $locale) { id diff --git a/v1/test/cases/testdata/v0/graphql/test-graphql-schema-is-valid.yaml b/v1/test/cases/testdata/v0/graphql/test-graphql-schema-is-valid.yaml index e7d12effa4..2b2465a748 100644 --- a/v1/test/cases/testdata/v0/graphql/test-graphql-schema-is-valid.yaml +++ b/v1/test/cases/testdata/v0/graphql/test-graphql-schema-is-valid.yaml @@ -398,6 +398,10 @@ cases: - | package test schema := ` + type Query { + x: Int + } + directive @directive(a: String = "b") on SCHEMA extend schema @directive ` p { diff --git a/v1/test/cases/testdata/v1/graphql/test-graphql-parse-and-verify.yaml b/v1/test/cases/testdata/v1/graphql/test-graphql-parse-and-verify.yaml index bb4f0950ef..71cecc8de5 100644 --- a/v1/test/cases/testdata/v1/graphql/test-graphql-parse-and-verify.yaml +++ b/v1/test/cases/testdata/v1/graphql/test-graphql-parse-and-verify.yaml @@ -79,6 +79,9 @@ cases: package test schema := ` + type Query { + x: Int + } extend type Query { myAction(myEnum: Locale!): SomeResult! } @@ -93,7 +96,6 @@ cases: ` query := ` - # Note: there is default enum value in variables query SomeOperation ($locale: Locale! = DE) { myAction(myEnum: $locale) { id diff --git a/v1/test/cases/testdata/v1/graphql/test-graphql-parse-query.yaml b/v1/test/cases/testdata/v1/graphql/test-graphql-parse-query.yaml index 9b9fe751c2..52ff5d0a79 100644 --- a/v1/test/cases/testdata/v1/graphql/test-graphql-parse-query.yaml +++ b/v1/test/cases/testdata/v1/graphql/test-graphql-parse-query.yaml @@ -417,11 +417,11 @@ cases: - | package test + # Copyright (c) 2015-present, Facebook, Inc. + # + # This source code is licensed under the MIT license found in the + # LICENSE file in the root directory of this source tree. gql := ` - # Copyright (c) 2015-present, Facebook, Inc. - # - # This source code is licensed under the MIT license found in the - # LICENSE file in the root directory of this source tree. query queryName($foo: ComplexType, $site: Site = MOBILE) { whoever123is: node(id: [123, 456]) { id , diff --git a/v1/test/cases/testdata/v1/graphql/test-graphql-parse-schema.yaml b/v1/test/cases/testdata/v1/graphql/test-graphql-parse-schema.yaml index ea94ae3a7d..05f75783e4 100644 --- a/v1/test/cases/testdata/v1/graphql/test-graphql-parse-schema.yaml +++ b/v1/test/cases/testdata/v1/graphql/test-graphql-parse-schema.yaml @@ -49,13 +49,15 @@ cases: """ Description """ - # Even with comments between them type Hello { world: String } + type Query { + hello: Hello + } ` - ast := {"Definitions": [{"BuiltIn": false, "Description": "Description", "Fields": [{"Description": "", "Name": "world", "Type": {"NamedType": "String", "NonNull": false}}], "Kind": "OBJECT", "Name": "Hello"}]} + ast := {"Definitions":[{"BuiltIn":false,"Description":"Description","Fields":[{"Description":"","Name":"world","Type":{"NamedType":"String","NonNull":false}}],"Kind":"OBJECT","Name":"Hello"},{"BuiltIn":false,"Description":"","Fields":[{"Description":"","Name":"hello","Type":{"NamedType":"Hello","NonNull":false}}],"Kind":"OBJECT","Name":"Query"}]} p if { graphql.parse_schema(gql) == ast diff --git a/v1/test/cases/testdata/v1/graphql/test-graphql-parse.yaml b/v1/test/cases/testdata/v1/graphql/test-graphql-parse.yaml index 47ee92ffe7..7495084dc1 100644 --- a/v1/test/cases/testdata/v1/graphql/test-graphql-parse.yaml +++ b/v1/test/cases/testdata/v1/graphql/test-graphql-parse.yaml @@ -78,6 +78,9 @@ cases: package test schema := ` + type Query { + x: Int + } extend type Query { myAction(myEnum: Locale!): SomeResult! } @@ -92,7 +95,6 @@ cases: ` query := ` - # Note: there is default enum value in variables query SomeOperation ($locale: Locale! = DE) { myAction(myEnum: $locale) { id diff --git a/v1/test/cases/testdata/v1/graphql/test-graphql-schema-is-valid.yaml b/v1/test/cases/testdata/v1/graphql/test-graphql-schema-is-valid.yaml index 6d828e085f..29175bec26 100644 --- a/v1/test/cases/testdata/v1/graphql/test-graphql-schema-is-valid.yaml +++ b/v1/test/cases/testdata/v1/graphql/test-graphql-schema-is-valid.yaml @@ -414,6 +414,10 @@ cases: package test schema := ` + type Query { + x: Int + } + directive @directive(a: String = "b") on SCHEMA extend schema @directive ` diff --git a/v1/topdown/graphql.go b/v1/topdown/graphql.go index c887041cd7..f5b6273ba7 100644 --- a/v1/topdown/graphql.go +++ b/v1/topdown/graphql.go @@ -9,12 +9,12 @@ import ( "fmt" "strings" - gqlast "github.com/open-policy-agent/opa/internal/gqlparser/ast" - gqlparser "github.com/open-policy-agent/opa/internal/gqlparser/parser" - gqlvalidator "github.com/open-policy-agent/opa/internal/gqlparser/validator" + gqlast "github.com/vektah/gqlparser/v2/ast" + gqlparser "github.com/vektah/gqlparser/v2/parser" + gqlvalidator "github.com/vektah/gqlparser/v2/validator" // Side-effecting import. Triggers GraphQL library's validation rule init() functions. - _ "github.com/open-policy-agent/opa/internal/gqlparser/validator/rules" + _ "github.com/vektah/gqlparser/v2/validator/rules" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/topdown/builtins" diff --git a/internal/gqlparser/LICENSE b/vendor/github.com/vektah/gqlparser/v2/LICENSE similarity index 100% rename from internal/gqlparser/LICENSE rename to vendor/github.com/vektah/gqlparser/v2/LICENSE diff --git a/internal/gqlparser/ast/argmap.go b/vendor/github.com/vektah/gqlparser/v2/ast/argmap.go similarity index 100% rename from internal/gqlparser/ast/argmap.go rename to vendor/github.com/vektah/gqlparser/v2/ast/argmap.go diff --git a/internal/gqlparser/ast/collections.go b/vendor/github.com/vektah/gqlparser/v2/ast/collections.go similarity index 100% rename from internal/gqlparser/ast/collections.go rename to vendor/github.com/vektah/gqlparser/v2/ast/collections.go diff --git a/vendor/github.com/vektah/gqlparser/v2/ast/comment.go b/vendor/github.com/vektah/gqlparser/v2/ast/comment.go new file mode 100644 index 0000000000..8fcfda5813 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/ast/comment.go @@ -0,0 +1,31 @@ +package ast + +import ( + "strconv" + "strings" +) + +type Comment struct { + Value string + Position *Position +} + +func (c *Comment) Text() string { + return strings.TrimPrefix(c.Value, "#") +} + +type CommentGroup struct { + List []*Comment +} + +func (c *CommentGroup) Dump() string { + if len(c.List) == 0 { + return "" + } + var builder strings.Builder + for _, comment := range c.List { + builder.WriteString(comment.Value) + builder.WriteString("\n") + } + return strconv.Quote(builder.String()) +} diff --git a/internal/gqlparser/ast/decode.go b/vendor/github.com/vektah/gqlparser/v2/ast/decode.go similarity index 99% rename from internal/gqlparser/ast/decode.go rename to vendor/github.com/vektah/gqlparser/v2/ast/decode.go index d00920554c..c9966b2440 100644 --- a/internal/gqlparser/ast/decode.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/decode.go @@ -11,7 +11,7 @@ func UnmarshalSelectionSet(b []byte) (SelectionSet, error) { return nil, err } - var result = make([]Selection, 0) + result := make([]Selection, 0) for _, item := range tmp { var field Field if err := json.Unmarshal(item, &field); err == nil { diff --git a/internal/gqlparser/ast/definition.go b/vendor/github.com/vektah/gqlparser/v2/ast/definition.go similarity index 84% rename from internal/gqlparser/ast/definition.go rename to vendor/github.com/vektah/gqlparser/v2/ast/definition.go index ee3d4df3ac..9ceebf1bee 100644 --- a/internal/gqlparser/ast/definition.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/definition.go @@ -31,6 +31,10 @@ type Definition struct { Position *Position `dump:"-" json:"-"` BuiltIn bool `dump:"-"` + + BeforeDescriptionComment *CommentGroup + AfterDescriptionComment *CommentGroup + EndOfDefinitionComment *CommentGroup } func (d *Definition) IsLeafType() bool { @@ -66,6 +70,9 @@ type FieldDefinition struct { Type *Type Directives DirectiveList Position *Position `dump:"-" json:"-"` + + BeforeDescriptionComment *CommentGroup + AfterDescriptionComment *CommentGroup } type ArgumentDefinition struct { @@ -75,6 +82,9 @@ type ArgumentDefinition struct { Type *Type Directives DirectiveList Position *Position `dump:"-" json:"-"` + + BeforeDescriptionComment *CommentGroup + AfterDescriptionComment *CommentGroup } type EnumValueDefinition struct { @@ -82,6 +92,9 @@ type EnumValueDefinition struct { Name string Directives DirectiveList Position *Position `dump:"-" json:"-"` + + BeforeDescriptionComment *CommentGroup + AfterDescriptionComment *CommentGroup } type DirectiveDefinition struct { @@ -91,4 +104,7 @@ type DirectiveDefinition struct { Locations []DirectiveLocation IsRepeatable bool Position *Position `dump:"-" json:"-"` + + BeforeDescriptionComment *CommentGroup + AfterDescriptionComment *CommentGroup } diff --git a/internal/gqlparser/ast/directive.go b/vendor/github.com/vektah/gqlparser/v2/ast/directive.go similarity index 100% rename from internal/gqlparser/ast/directive.go rename to vendor/github.com/vektah/gqlparser/v2/ast/directive.go diff --git a/internal/gqlparser/ast/document.go b/vendor/github.com/vektah/gqlparser/v2/ast/document.go similarity index 86% rename from internal/gqlparser/ast/document.go rename to vendor/github.com/vektah/gqlparser/v2/ast/document.go index 4a6654b9aa..e2520ffb7c 100644 --- a/internal/gqlparser/ast/document.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/document.go @@ -4,6 +4,7 @@ type QueryDocument struct { Operations OperationList Fragments FragmentDefinitionList Position *Position `dump:"-" json:"-"` + Comment *CommentGroup } type SchemaDocument struct { @@ -13,6 +14,7 @@ type SchemaDocument struct { Definitions DefinitionList Extensions DefinitionList Position *Position `dump:"-" json:"-"` + Comment *CommentGroup } func (d *SchemaDocument) Merge(other *SchemaDocument) { @@ -24,9 +26,10 @@ func (d *SchemaDocument) Merge(other *SchemaDocument) { } type Schema struct { - Query *Definition - Mutation *Definition - Subscription *Definition + Query *Definition + Mutation *Definition + Subscription *Definition + SchemaDirectives DirectiveList Types map[string]*Definition Directives map[string]*DirectiveDefinition @@ -35,6 +38,8 @@ type Schema struct { Implements map[string][]*Definition Description string + + Comment *CommentGroup } // AddTypes is the helper to add types definition to the schema @@ -70,10 +75,15 @@ type SchemaDefinition struct { Directives DirectiveList OperationTypes OperationTypeDefinitionList Position *Position `dump:"-" json:"-"` + + BeforeDescriptionComment *CommentGroup + AfterDescriptionComment *CommentGroup + EndOfDefinitionComment *CommentGroup } type OperationTypeDefinition struct { Operation Operation Type string Position *Position `dump:"-" json:"-"` + Comment *CommentGroup } diff --git a/internal/gqlparser/ast/dumper.go b/vendor/github.com/vektah/gqlparser/v2/ast/dumper.go similarity index 88% rename from internal/gqlparser/ast/dumper.go rename to vendor/github.com/vektah/gqlparser/v2/ast/dumper.go index 84266a618f..e9ea88a12a 100644 --- a/internal/gqlparser/ast/dumper.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/dumper.go @@ -40,13 +40,13 @@ func (d *dumper) dump(v reflect.Value) { d.WriteString("false") } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - d.WriteString(strconv.FormatInt(v.Int(), 10)) + fmt.Fprintf(d, "%d", v.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - d.WriteString(strconv.FormatUint(v.Uint(), 10)) + fmt.Fprintf(d, "%d", v.Uint()) case reflect.Float32, reflect.Float64: - d.WriteString(fmt.Sprintf("%.2f", v.Float())) + fmt.Fprintf(d, "%.2f", v.Float()) case reflect.String: if v.Type().Name() != "string" { @@ -70,11 +70,11 @@ func (d *dumper) dump(v reflect.Value) { } func (d *dumper) writeIndent() { - d.Buffer.WriteString(strings.Repeat(" ", d.indent)) + d.WriteString(strings.Repeat(" ", d.indent)) } func (d *dumper) nl() { - d.Buffer.WriteByte('\n') + d.WriteByte('\n') d.writeIndent() } @@ -88,7 +88,7 @@ func typeName(t reflect.Type) string { func (d *dumper) dumpArray(v reflect.Value) { d.WriteString("[" + typeName(v.Type().Elem()) + "]") - for i := range v.Len() { + for i := 0; i < v.Len(); i++ { d.nl() d.WriteString("- ") d.indent++ @@ -102,7 +102,7 @@ func (d *dumper) dumpStruct(v reflect.Value) { d.indent++ typ := v.Type() - for i := range v.NumField() { + for i := 0; i < v.NumField(); i++ { f := v.Field(i) if typ.Field(i).Tag.Get("dump") == "-" { continue @@ -132,13 +132,13 @@ func isZero(v reflect.Value) bool { return true } z := true - for i := range v.Len() { + for i := 0; i < v.Len(); i++ { z = z && isZero(v.Index(i)) } return z case reflect.Struct: z := true - for i := range v.NumField() { + for i := 0; i < v.NumField(); i++ { z = z && isZero(v.Field(i)) } return z diff --git a/internal/gqlparser/ast/fragment.go b/vendor/github.com/vektah/gqlparser/v2/ast/fragment.go similarity index 91% rename from internal/gqlparser/ast/fragment.go rename to vendor/github.com/vektah/gqlparser/v2/ast/fragment.go index 723d833999..05805e1085 100644 --- a/internal/gqlparser/ast/fragment.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/fragment.go @@ -9,6 +9,7 @@ type FragmentSpread struct { Definition *FragmentDefinition Position *Position `dump:"-" json:"-"` + Comment *CommentGroup } type InlineFragment struct { @@ -20,6 +21,7 @@ type InlineFragment struct { ObjectDefinition *Definition Position *Position `dump:"-" json:"-"` + Comment *CommentGroup } type FragmentDefinition struct { @@ -35,4 +37,5 @@ type FragmentDefinition struct { Definition *Definition Position *Position `dump:"-" json:"-"` + Comment *CommentGroup } diff --git a/internal/gqlparser/ast/operation.go b/vendor/github.com/vektah/gqlparser/v2/ast/operation.go similarity index 91% rename from internal/gqlparser/ast/operation.go rename to vendor/github.com/vektah/gqlparser/v2/ast/operation.go index 5fc2f3b246..2efed025ba 100644 --- a/internal/gqlparser/ast/operation.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/operation.go @@ -15,6 +15,7 @@ type OperationDefinition struct { Directives DirectiveList SelectionSet SelectionSet Position *Position `dump:"-" json:"-"` + Comment *CommentGroup } type VariableDefinition struct { @@ -23,6 +24,7 @@ type VariableDefinition struct { DefaultValue *Value Directives DirectiveList Position *Position `dump:"-" json:"-"` + Comment *CommentGroup // Requires validation Definition *Definition diff --git a/internal/gqlparser/ast/path.go b/vendor/github.com/vektah/gqlparser/v2/ast/path.go similarity index 91% rename from internal/gqlparser/ast/path.go rename to vendor/github.com/vektah/gqlparser/v2/ast/path.go index be1a9e4edb..f40aa953dd 100644 --- a/internal/gqlparser/ast/path.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/path.go @@ -14,10 +14,15 @@ type PathElement interface { isPathElement() } -var _ PathElement = PathIndex(0) -var _ PathElement = PathName("") +var ( + _ PathElement = PathIndex(0) + _ PathElement = PathName("") +) func (path Path) String() string { + if path == nil { + return "" + } var str bytes.Buffer for i, v := range path { switch v := v.(type) { diff --git a/internal/gqlparser/ast/selection.go b/vendor/github.com/vektah/gqlparser/v2/ast/selection.go similarity index 70% rename from internal/gqlparser/ast/selection.go rename to vendor/github.com/vektah/gqlparser/v2/ast/selection.go index 677300edd5..1858dc2136 100644 --- a/internal/gqlparser/ast/selection.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/selection.go @@ -11,9 +11,9 @@ func (*Field) isSelection() {} func (*FragmentSpread) isSelection() {} func (*InlineFragment) isSelection() {} -func (s *Field) GetPosition() *Position { return s.Position } +func (f *Field) GetPosition() *Position { return f.Position } func (s *FragmentSpread) GetPosition() *Position { return s.Position } -func (s *InlineFragment) GetPosition() *Position { return s.Position } +func (f *InlineFragment) GetPosition() *Position { return f.Position } type Field struct { Alias string @@ -22,6 +22,7 @@ type Field struct { Directives DirectiveList SelectionSet SelectionSet Position *Position `dump:"-" json:"-"` + Comment *CommentGroup // Require validation Definition *FieldDefinition @@ -32,8 +33,9 @@ type Argument struct { Name string Value *Value Position *Position `dump:"-" json:"-"` + Comment *CommentGroup } -func (s *Field) ArgumentMap(vars map[string]interface{}) map[string]interface{} { - return arg2map(s.Definition.Arguments, s.Arguments, vars) +func (f *Field) ArgumentMap(vars map[string]interface{}) map[string]interface{} { + return arg2map(f.Definition.Arguments, f.Arguments, vars) } diff --git a/internal/gqlparser/ast/source.go b/vendor/github.com/vektah/gqlparser/v2/ast/source.go similarity index 100% rename from internal/gqlparser/ast/source.go rename to vendor/github.com/vektah/gqlparser/v2/ast/source.go diff --git a/internal/gqlparser/ast/type.go b/vendor/github.com/vektah/gqlparser/v2/ast/type.go similarity index 100% rename from internal/gqlparser/ast/type.go rename to vendor/github.com/vektah/gqlparser/v2/ast/type.go diff --git a/internal/gqlparser/ast/value.go b/vendor/github.com/vektah/gqlparser/v2/ast/value.go similarity index 98% rename from internal/gqlparser/ast/value.go rename to vendor/github.com/vektah/gqlparser/v2/ast/value.go index ae23a98d7d..45fa8016b5 100644 --- a/internal/gqlparser/ast/value.go +++ b/vendor/github.com/vektah/gqlparser/v2/ast/value.go @@ -26,6 +26,7 @@ type Value struct { Children ChildValueList Kind ValueKind Position *Position `dump:"-" json:"-"` + Comment *CommentGroup // Require validation Definition *Definition @@ -37,6 +38,7 @@ type ChildValue struct { Name string Value *Value Position *Position `dump:"-" json:"-"` + Comment *CommentGroup } func (v *Value) Value(vars map[string]interface{}) (interface{}, error) { diff --git a/internal/gqlparser/gqlerror/error.go b/vendor/github.com/vektah/gqlparser/v2/gqlerror/error.go similarity index 75% rename from internal/gqlparser/gqlerror/error.go rename to vendor/github.com/vektah/gqlparser/v2/gqlerror/error.go index 58d1c1bd6c..ca9036ca7e 100644 --- a/internal/gqlparser/gqlerror/error.go +++ b/vendor/github.com/vektah/gqlparser/v2/gqlerror/error.go @@ -1,17 +1,17 @@ package gqlerror import ( - "bytes" "errors" "fmt" "strconv" + "strings" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" ) -// Error is the standard graphql error type described in https://facebook.github.io/graphql/draft/#sec-Errors +// Error is the standard graphql error type described in https://spec.graphql.org/draft/#sec-Errors type Error struct { - err error `json:"-"` + Err error `json:"-"` Message string `json:"message"` Path ast.Path `json:"path,omitempty"` Locations []Location `json:"locations,omitempty"` @@ -38,7 +38,7 @@ type Location struct { type List []*Error func (err *Error) Error() string { - var res bytes.Buffer + var res strings.Builder if err == nil { return "" } @@ -66,16 +66,23 @@ func (err *Error) Error() string { return res.String() } -func (err Error) pathString() string { +func (err *Error) pathString() string { return err.Path.String() } -func (err Error) Unwrap() error { - return err.err +func (err *Error) Unwrap() error { + return err.Err +} + +func (err *Error) AsError() error { + if err == nil { + return nil + } + return err } func (errs List) Error() string { - var buf bytes.Buffer + var buf strings.Builder for _, err := range errs { buf.WriteString(err.Error()) buf.WriteByte('\n') @@ -101,14 +108,48 @@ func (errs List) As(target interface{}) bool { return false } +func (errs List) Unwrap() []error { + l := make([]error, len(errs)) + for i, err := range errs { + l[i] = err + } + return l +} + func WrapPath(path ast.Path, err error) *Error { + if err == nil { + return nil + } return &Error{ - err: err, + Err: err, Message: err.Error(), Path: path, } } +func Wrap(err error) *Error { + if err == nil { + return nil + } + return &Error{ + Err: err, + Message: err.Error(), + } +} + +func WrapIfUnwrapped(err error) *Error { + if err == nil { + return nil + } + if gqlErr, ok := err.(*Error); ok { + return gqlErr + } + return &Error{ + Err: err, + Message: err.Error(), + } +} + func Errorf(message string, args ...interface{}) *Error { return &Error{ Message: fmt.Sprintf(message, args...), diff --git a/internal/gqlparser/lexer/blockstring.go b/vendor/github.com/vektah/gqlparser/v2/lexer/blockstring.go similarity index 100% rename from internal/gqlparser/lexer/blockstring.go rename to vendor/github.com/vektah/gqlparser/v2/lexer/blockstring.go diff --git a/internal/gqlparser/lexer/lexer.go b/vendor/github.com/vektah/gqlparser/v2/lexer/lexer.go similarity index 91% rename from internal/gqlparser/lexer/lexer.go rename to vendor/github.com/vektah/gqlparser/v2/lexer/lexer.go index f25555e650..1cbb4a0308 100644 --- a/internal/gqlparser/lexer/lexer.go +++ b/vendor/github.com/vektah/gqlparser/v2/lexer/lexer.go @@ -4,8 +4,8 @@ import ( "bytes" "unicode/utf8" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - "github.com/open-policy-agent/opa/internal/gqlparser/gqlerror" + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/gqlerror" ) // Lexer turns graphql request and schema strings into tokens @@ -55,7 +55,7 @@ func (s *Lexer) makeValueToken(kind Type, value string) (Token, error) { }, nil } -func (s *Lexer) makeError(format string, args ...interface{}) (Token, error) { +func (s *Lexer) makeError(format string, args ...interface{}) (Token, *gqlerror.Error) { column := s.endRunes - s.lineStartRunes + 1 return Token{ Kind: Invalid, @@ -66,7 +66,7 @@ func (s *Lexer) makeError(format string, args ...interface{}) (Token, error) { Column: column, Src: s.Source, }, - }, gqlerror.ErrorLocf(s.Source.Name, s.line, column, format, args...) + }, gqlerror.ErrorLocf(s.Name, s.line, column, format, args...) } // ReadToken gets the next token from the source starting at the given position. @@ -74,8 +74,7 @@ func (s *Lexer) makeError(format string, args ...interface{}) (Token, error) { // This skips over whitespace and comments until it finds the next lexable // token, then lexes punctuators immediately or calls the appropriate helper // function for more complicated tokens. -func (s *Lexer) ReadToken() (token Token, err error) { - +func (s *Lexer) ReadToken() (Token, error) { s.ws() s.start = s.end s.startRunes = s.endRunes @@ -121,10 +120,7 @@ func (s *Lexer) ReadToken() (token Token, err error) { case '|': return s.makeValueToken(Pipe, "") case '#': - if comment, err := s.readComment(); err != nil { - return comment, err - } - return s.ReadToken() + return s.readComment() case '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z': return s.readName() @@ -258,7 +254,6 @@ func (s *Lexer) readNumber() (Token, error) { return s.makeToken(Float) } return s.makeToken(Int) - } // acceptByte if it matches any of given bytes, returning true if it found anything @@ -321,8 +316,8 @@ func (s *Lexer) readString() (Token, error) { } switch r { default: - var char = rune(r) - var w = 1 + char := rune(r) + w := 1 // skip unicode overhead if we are in the ascii range if r >= 127 { @@ -426,17 +421,29 @@ func (s *Lexer) readBlockString() (Token, error) { r := s.Input[s.end] // Closing triple quote (""") - if r == '"' && s.end+3 <= inputLen && s.Input[s.end:s.end+3] == `"""` { - t, err := s.makeValueToken(BlockString, blockStringValue(buf.String())) + if r == '"' { + // Count consecutive quotes + quoteCount := 1 + i := s.end + 1 + for i < inputLen && s.Input[i] == '"' { + quoteCount++ + i++ + } - // the token should not include the quotes in its value, but should cover them in its position - t.Pos.Start -= 3 - t.Pos.End += 3 + // If we have at least 3 quotes, use the last 3 as the closing quote + if quoteCount >= 3 { + // Add any extra quotes to the buffer (except the last 3) + for j := 0; j < quoteCount-3; j++ { + buf.WriteByte('"') + } - // skip the close quote - s.end += 3 - s.endRunes += 3 - return t, err + t, err := s.makeValueToken(BlockString, blockStringValue(buf.String())) + t.Pos.Start -= 3 + t.Pos.End += 3 + s.end += quoteCount + s.endRunes += quoteCount + return t, err + } } // SourceCharacter @@ -444,11 +451,12 @@ func (s *Lexer) readBlockString() (Token, error) { return s.makeError(`Invalid character within String: "\u%04d".`, r) } - if r == '\\' && s.end+4 <= inputLen && s.Input[s.end:s.end+4] == `\"""` { + switch { + case r == '\\' && s.end+4 <= inputLen && s.Input[s.end:s.end+4] == `\"""`: buf.WriteString(`"""`) s.end += 4 s.endRunes += 4 - } else if r == '\r' { + case r == '\r': if s.end+1 < inputLen && s.Input[s.end+1] == '\n' { s.end++ s.endRunes++ @@ -459,9 +467,9 @@ func (s *Lexer) readBlockString() (Token, error) { s.endRunes++ s.line++ s.lineStartRunes = s.endRunes - } else { - var char = rune(r) - var w = 1 + default: + char := rune(r) + w := 1 // skip unicode overhead if we are in the ascii range if r >= 127 { diff --git a/internal/gqlparser/lexer/lexer_test.yml b/vendor/github.com/vektah/gqlparser/v2/lexer/lexer_test.yml similarity index 91% rename from internal/gqlparser/lexer/lexer_test.yml rename to vendor/github.com/vektah/gqlparser/v2/lexer/lexer_test.yml index 5c4d5f0ff5..0899f4ca9b 100644 --- a/internal/gqlparser/lexer/lexer_test.yml +++ b/vendor/github.com/vektah/gqlparser/v2/lexer/lexer_test.yml @@ -26,6 +26,31 @@ simple tokens: column: 3 value: 'foo' + - name: records line and column with comments + input: "\n\n\n#foo\n #bar\n foo\n" + tokens: + - + kind: COMMENT + start: 3 + end: 7 + line: 4 + column: 0 + value: '#foo' + - + kind: COMMENT + start: 10 + end: 14 + line: 5 + column: 3 + value: '#bar' + - + kind: NAME + start: 17 + end: 20 + line: 6 + column: 3 + value: 'foo' + - name: skips whitespace input: "\n\n foo\n\n\n" tokens: @@ -35,15 +60,6 @@ simple tokens: end: 9 value: 'foo' - - name: skips comments - input: "\n #comment\n foo#comment\n" - tokens: - - - kind: NAME - start: 18 - end: 21 - value: 'foo' - - name: skips commas input: ",,,foo,,," tokens: @@ -78,6 +94,57 @@ simple tokens: end: 1 value: a +lexes comments: + - name: basic + input: '#simple' + tokens: + - + kind: COMMENT + start: 0 + end: 7 + value: '#simple' + + - name: two lines + input: "#first\n#second" + tokens: + - + kind: COMMENT + start: 0 + end: 6 + value: "#first" + - + kind: COMMENT + start: 7 + end: 14 + value: "#second" + + - name: whitespace + input: '# white space ' + tokens: + - + kind: COMMENT + start: 0 + end: 14 + value: '# white space ' + + - name: not escaped + input: '#not escaped \n\r\b\t\f' + tokens: + - + kind: COMMENT + start: 0 + end: 23 + value: '#not escaped \n\r\b\t\f' + + - name: slashes + input: '#slashes \\ \/' + tokens: + - + kind: COMMENT + start: 0 + end: 14 + value: '#slashes \\ \/' + lexes strings: - name: basic input: '"simple"' @@ -674,7 +741,6 @@ lex reports useful unknown character error: - name: question mark input: "?" error: - message: 'Cannot parse the unexpected character "?".' message: 'Cannot parse the unexpected character "?".' locations: [{ line: 1, column: 1 }] diff --git a/internal/gqlparser/lexer/token.go b/vendor/github.com/vektah/gqlparser/v2/lexer/token.go similarity index 97% rename from internal/gqlparser/lexer/token.go rename to vendor/github.com/vektah/gqlparser/v2/lexer/token.go index 79eefd0f4e..8985a7efb7 100644 --- a/internal/gqlparser/lexer/token.go +++ b/vendor/github.com/vektah/gqlparser/v2/lexer/token.go @@ -3,7 +3,7 @@ package lexer import ( "strconv" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" ) const ( diff --git a/internal/gqlparser/parser/parser.go b/vendor/github.com/vektah/gqlparser/v2/parser/parser.go similarity index 54% rename from internal/gqlparser/parser/parser.go rename to vendor/github.com/vektah/gqlparser/v2/parser/parser.go index c0d2b4a3b7..bfcf7ea498 100644 --- a/internal/gqlparser/parser/parser.go +++ b/vendor/github.com/vektah/gqlparser/v2/parser/parser.go @@ -1,11 +1,12 @@ package parser import ( + "fmt" "strconv" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - "github.com/open-policy-agent/opa/internal/gqlparser/gqlerror" - "github.com/open-policy-agent/opa/internal/gqlparser/lexer" + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/gqlerror" + "github.com/vektah/gqlparser/v2/lexer" ) type parser struct { @@ -17,6 +18,53 @@ type parser struct { peekError error prev lexer.Token + + comment *ast.CommentGroup + commentConsuming bool + + tokenCount int + maxTokenLimit int +} + +func (p *parser) SetMaxTokenLimit(maxToken int) { + p.maxTokenLimit = maxToken +} + +func (p *parser) consumeComment() (*ast.Comment, bool) { + if p.err != nil { + return nil, false + } + tok := p.peek() + if tok.Kind != lexer.Comment { + return nil, false + } + p.next() + return &ast.Comment{ + Value: tok.Value, + Position: &tok.Pos, + }, true +} + +func (p *parser) consumeCommentGroup() { + if p.err != nil { + return + } + if p.commentConsuming { + return + } + p.commentConsuming = true + + var comments []*ast.Comment + for { + comment, ok := p.consumeComment() + if !ok { + break + } + comments = append(comments, comment) + } + + p.comment = &ast.CommentGroup{List: comments} + p.commentConsuming = false } func (p *parser) peekPos() *ast.Position { @@ -36,6 +84,9 @@ func (p *parser) peek() lexer.Token { if !p.peeked { p.peekToken, p.peekError = p.lexer.ReadToken() p.peeked = true + if p.peekToken.Kind == lexer.Comment { + p.consumeCommentGroup() + } } return p.peekToken @@ -52,33 +103,45 @@ func (p *parser) next() lexer.Token { if p.err != nil { return p.prev } + // Increment the token count before reading the next token + p.tokenCount++ + if p.maxTokenLimit != 0 && p.tokenCount > p.maxTokenLimit { + p.err = fmt.Errorf("exceeded token limit of %d", p.maxTokenLimit) + return p.prev + } if p.peeked { p.peeked = false + p.comment = nil p.prev, p.err = p.peekToken, p.peekError } else { p.prev, p.err = p.lexer.ReadToken() + if p.prev.Kind == lexer.Comment { + p.consumeCommentGroup() + } } return p.prev } -func (p *parser) expectKeyword(value string) lexer.Token { +func (p *parser) expectKeyword(value string) (lexer.Token, *ast.CommentGroup) { tok := p.peek() + comment := p.comment if tok.Kind == lexer.Name && tok.Value == value { - return p.next() + return p.next(), comment } p.error(tok, "Expected %s, found %s", strconv.Quote(value), tok.String()) - return tok + return tok, comment } -func (p *parser) expect(kind lexer.Type) lexer.Token { +func (p *parser) expect(kind lexer.Type) (lexer.Token, *ast.CommentGroup) { tok := p.peek() + comment := p.comment if tok.Kind == kind { - return p.next() + return p.next(), comment } p.error(tok, "Expected %s, found %s", kind, tok.Kind.String()) - return tok + return tok, comment } func (p *parser) skip(kind lexer.Type) bool { @@ -115,10 +178,10 @@ func (p *parser) many(start lexer.Type, end lexer.Type, cb func()) { p.next() } -func (p *parser) some(start lexer.Type, end lexer.Type, cb func()) { +func (p *parser) some(start lexer.Type, end lexer.Type, cb func()) *ast.CommentGroup { hasDef := p.skip(start) if !hasDef { - return + return nil } called := false @@ -129,8 +192,10 @@ func (p *parser) some(start lexer.Type, end lexer.Type, cb func()) { if !called { p.error(p.peek(), "expected at least one definition, found %s", p.peek().Kind.String()) - return + return nil } + comment := p.comment p.next() + return comment } diff --git a/internal/gqlparser/parser/query.go b/vendor/github.com/vektah/gqlparser/v2/parser/query.go similarity index 85% rename from internal/gqlparser/parser/query.go rename to vendor/github.com/vektah/gqlparser/v2/parser/query.go index 319425f587..47ac214a91 100644 --- a/internal/gqlparser/parser/query.go +++ b/vendor/github.com/vektah/gqlparser/v2/parser/query.go @@ -1,15 +1,23 @@ package parser import ( - "github.com/open-policy-agent/opa/internal/gqlparser/lexer" + "github.com/vektah/gqlparser/v2/lexer" - //nolint:revive - . "github.com/open-policy-agent/opa/internal/gqlparser/ast" + . "github.com/vektah/gqlparser/v2/ast" //nolint:staticcheck // bad, yeah ) func ParseQuery(source *Source) (*QueryDocument, error) { p := parser{ - lexer: lexer.New(source), + lexer: lexer.New(source), + maxTokenLimit: 0, // 0 means unlimited + } + return p.parseQueryDocument(), p.err +} + +func ParseQueryWithTokenLimit(source *Source, maxTokenLimit int) (*QueryDocument, error) { + p := parser{ + lexer: lexer.New(source), + maxTokenLimit: maxTokenLimit, } return p.parseQueryDocument(), p.err } @@ -45,6 +53,7 @@ func (p *parser) parseOperationDefinition() *OperationDefinition { if p.peek().Kind == lexer.BraceL { return &OperationDefinition{ Position: p.peekPos(), + Comment: p.comment, Operation: Query, SelectionSet: p.parseRequiredSelectionSet(), } @@ -52,6 +61,7 @@ func (p *parser) parseOperationDefinition() *OperationDefinition { var od OperationDefinition od.Position = p.peekPos() + od.Comment = p.comment od.Operation = p.parseOperationType() if p.peek().Kind == lexer.Name { @@ -81,7 +91,7 @@ func (p *parser) parseOperationType() Operation { func (p *parser) parseVariableDefinitions() VariableDefinitionList { var defs []*VariableDefinition - p.many(lexer.ParenL, lexer.ParenR, func() { + p.some(lexer.ParenL, lexer.ParenR, func() { defs = append(defs, p.parseVariableDefinition()) }) @@ -91,6 +101,7 @@ func (p *parser) parseVariableDefinitions() VariableDefinitionList { func (p *parser) parseVariableDefinition() *VariableDefinition { var def VariableDefinition def.Position = p.peekPos() + def.Comment = p.comment def.Variable = p.parseVariable() p.expect(lexer.Colon) @@ -117,7 +128,7 @@ func (p *parser) parseOptionalSelectionSet() SelectionSet { selections = append(selections, p.parseSelection()) }) - return SelectionSet(selections) + return selections } func (p *parser) parseRequiredSelectionSet() SelectionSet { @@ -131,7 +142,7 @@ func (p *parser) parseRequiredSelectionSet() SelectionSet { selections = append(selections, p.parseSelection()) }) - return SelectionSet(selections) + return selections } func (p *parser) parseSelection() Selection { @@ -144,6 +155,7 @@ func (p *parser) parseSelection() Selection { func (p *parser) parseField() *Field { var field Field field.Position = p.peekPos() + field.Comment = p.comment field.Alias = p.parseName() if p.skip(lexer.Colon) { @@ -163,7 +175,7 @@ func (p *parser) parseField() *Field { func (p *parser) parseArguments(isConst bool) ArgumentList { var arguments ArgumentList - p.many(lexer.ParenL, lexer.ParenR, func() { + p.some(lexer.ParenL, lexer.ParenR, func() { arguments = append(arguments, p.parseArgument(isConst)) }) @@ -173,6 +185,7 @@ func (p *parser) parseArguments(isConst bool) ArgumentList { func (p *parser) parseArgument(isConst bool) *Argument { arg := Argument{} arg.Position = p.peekPos() + arg.Comment = p.comment arg.Name = p.parseName() p.expect(lexer.Colon) @@ -181,11 +194,12 @@ func (p *parser) parseArgument(isConst bool) *Argument { } func (p *parser) parseFragment() Selection { - p.expect(lexer.Spread) + _, comment := p.expect(lexer.Spread) if peek := p.peek(); peek.Kind == lexer.Name && peek.Value != "on" { return &FragmentSpread{ Position: p.peekPos(), + Comment: comment, Name: p.parseFragmentName(), Directives: p.parseDirectives(false), } @@ -193,6 +207,7 @@ func (p *parser) parseFragment() Selection { var def InlineFragment def.Position = p.peekPos() + def.Comment = comment if p.peek().Value == "on" { p.next() // "on" @@ -207,6 +222,7 @@ func (p *parser) parseFragment() Selection { func (p *parser) parseFragmentDefinition() *FragmentDefinition { var def FragmentDefinition def.Position = p.peekPos() + def.Comment = p.comment p.expectKeyword("fragment") def.Name = p.parseFragmentName() @@ -243,7 +259,7 @@ func (p *parser) parseValueLiteral(isConst bool) *Value { p.unexpectedError() return nil } - return &Value{Position: &token.Pos, Raw: p.parseVariable(), Kind: Variable} + return &Value{Position: &token.Pos, Comment: p.comment, Raw: p.parseVariable(), Kind: Variable} case lexer.Int: kind = IntValue case lexer.Float: @@ -268,32 +284,35 @@ func (p *parser) parseValueLiteral(isConst bool) *Value { p.next() - return &Value{Position: &token.Pos, Raw: token.Value, Kind: kind} + return &Value{Position: &token.Pos, Comment: p.comment, Raw: token.Value, Kind: kind} } func (p *parser) parseList(isConst bool) *Value { var values ChildValueList pos := p.peekPos() + comment := p.comment p.many(lexer.BracketL, lexer.BracketR, func() { values = append(values, &ChildValue{Value: p.parseValueLiteral(isConst)}) }) - return &Value{Children: values, Kind: ListValue, Position: pos} + return &Value{Children: values, Kind: ListValue, Position: pos, Comment: comment} } func (p *parser) parseObject(isConst bool) *Value { var fields ChildValueList pos := p.peekPos() + comment := p.comment p.many(lexer.BraceL, lexer.BraceR, func() { fields = append(fields, p.parseObjectField(isConst)) }) - return &Value{Children: fields, Kind: ObjectValue, Position: pos} + return &Value{Children: fields, Kind: ObjectValue, Position: pos, Comment: comment} } func (p *parser) parseObjectField(isConst bool) *ChildValue { field := ChildValue{} field.Position = p.peekPos() + field.Comment = p.comment field.Name = p.parseName() p.expect(lexer.Colon) @@ -343,7 +362,7 @@ func (p *parser) parseTypeReference() *Type { } func (p *parser) parseName() string { - token := p.expect(lexer.Name) + token, _ := p.expect(lexer.Name) return token.Value } diff --git a/internal/gqlparser/parser/query_test.yml b/vendor/github.com/vektah/gqlparser/v2/parser/query_test.yml similarity index 98% rename from internal/gqlparser/parser/query_test.yml rename to vendor/github.com/vektah/gqlparser/v2/parser/query_test.yml index a46a01e718..ec0580f5fa 100644 --- a/internal/gqlparser/parser/query_test.yml +++ b/vendor/github.com/vektah/gqlparser/v2/parser/query_test.yml @@ -436,6 +436,7 @@ large queries: - Alias: "id" Name: "id" + Comment: "# Copyright (c) 2015-present, Facebook, Inc.\n#\n# This source code is licensed under the MIT license found in the\n# LICENSE file in the root directory of this source tree.\n" - Operation: Operation("mutation") Name: "likeStory" diff --git a/internal/gqlparser/parser/schema.go b/vendor/github.com/vektah/gqlparser/v2/parser/schema.go similarity index 58% rename from internal/gqlparser/parser/schema.go rename to vendor/github.com/vektah/gqlparser/v2/parser/schema.go index 32c293399b..804f02c9f8 100644 --- a/internal/gqlparser/parser/schema.go +++ b/vendor/github.com/vektah/gqlparser/v2/parser/schema.go @@ -1,40 +1,72 @@ package parser import ( - //nolint:revive - . "github.com/open-policy-agent/opa/internal/gqlparser/ast" - "github.com/open-policy-agent/opa/internal/gqlparser/lexer" + . "github.com/vektah/gqlparser/v2/ast" //nolint:staticcheck // bad, yeah + "github.com/vektah/gqlparser/v2/lexer" ) -func ParseSchema(source *Source) (*SchemaDocument, error) { - p := parser{ - lexer: lexer.New(source), - } - ast, err := p.parseSchemaDocument(), p.err - if err != nil { - return nil, err - } - - for _, def := range ast.Definitions { - def.BuiltIn = source.BuiltIn - } - for _, def := range ast.Extensions { - def.BuiltIn = source.BuiltIn - } - - return ast, nil -} - func ParseSchemas(inputs ...*Source) (*SchemaDocument, error) { - ast := &SchemaDocument{} + sd := &SchemaDocument{} for _, input := range inputs { inputAst, err := ParseSchema(input) if err != nil { return nil, err } - ast.Merge(inputAst) + sd.Merge(inputAst) } - return ast, nil + return sd, nil +} + +func ParseSchema(source *Source) (*SchemaDocument, error) { + p := parser{ + lexer: lexer.New(source), + maxTokenLimit: 0, // default value is unlimited + } + sd, err := p.parseSchemaDocument(), p.err + if err != nil { + return nil, err + } + + for _, def := range sd.Definitions { + def.BuiltIn = source.BuiltIn + } + for _, def := range sd.Extensions { + def.BuiltIn = source.BuiltIn + } + + return sd, nil +} + +func ParseSchemasWithLimit(maxTokenLimit int, inputs ...*Source) (*SchemaDocument, error) { + sd := &SchemaDocument{} + for _, input := range inputs { + inputAst, err := ParseSchemaWithLimit(input, maxTokenLimit) + if err != nil { + return nil, err + } + sd.Merge(inputAst) + } + return sd, nil +} + +func ParseSchemaWithLimit(source *Source, maxTokenLimit int) (*SchemaDocument, error) { + p := parser{ + lexer: lexer.New(source), + maxTokenLimit: maxTokenLimit, // 0 is unlimited + } + sd, err := p.parseSchemaDocument(), p.err + if err != nil { + return nil, err + } + + for _, def := range sd.Definitions { + def.BuiltIn = source.BuiltIn + } + for _, def := range sd.Extensions { + def.BuiltIn = source.BuiltIn + } + + return sd, nil } func (p *parser) parseSchemaDocument() *SchemaDocument { @@ -45,7 +77,7 @@ func (p *parser) parseSchemaDocument() *SchemaDocument { return nil } - var description string + var description descriptionWithComment if p.peek().Kind == lexer.BlockString || p.peek().Kind == lexer.String { description = p.parseDescription() } @@ -63,7 +95,7 @@ func (p *parser) parseSchemaDocument() *SchemaDocument { case "directive": doc.Directives = append(doc.Directives, p.parseDirectiveDefinition(description)) case "extend": - if description != "" { + if description.text != "" { p.unexpectedToken(p.prev) } p.parseTypeSystemExtension(&doc) @@ -73,20 +105,26 @@ func (p *parser) parseSchemaDocument() *SchemaDocument { } } + // treat end of file comments + doc.Comment = p.comment + return &doc } -func (p *parser) parseDescription() string { +func (p *parser) parseDescription() descriptionWithComment { token := p.peek() + var desc descriptionWithComment if token.Kind != lexer.BlockString && token.Kind != lexer.String { - return "" + return desc } - return p.next().Value + desc.comment = p.comment + desc.text = p.next().Value + return desc } -func (p *parser) parseTypeSystemDefinition(description string) *Definition { +func (p *parser) parseTypeSystemDefinition(description descriptionWithComment) *Definition { tok := p.peek() if tok.Kind != lexer.Name { p.unexpectedError() @@ -112,15 +150,17 @@ func (p *parser) parseTypeSystemDefinition(description string) *Definition { } } -func (p *parser) parseSchemaDefinition(description string) *SchemaDefinition { - p.expectKeyword("schema") +func (p *parser) parseSchemaDefinition(description descriptionWithComment) *SchemaDefinition { + _, comment := p.expectKeyword("schema") - def := SchemaDefinition{Description: description} + def := SchemaDefinition{} def.Position = p.peekPos() - def.Description = description + def.BeforeDescriptionComment = description.comment + def.Description = description.text + def.AfterDescriptionComment = comment def.Directives = p.parseDirectives(true) - p.some(lexer.BraceL, lexer.BraceR, func() { + def.EndOfDefinitionComment = p.some(lexer.BraceL, lexer.BraceR, func() { def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition()) }) return &def @@ -129,35 +169,40 @@ func (p *parser) parseSchemaDefinition(description string) *SchemaDefinition { func (p *parser) parseOperationTypeDefinition() *OperationTypeDefinition { var op OperationTypeDefinition op.Position = p.peekPos() + op.Comment = p.comment op.Operation = p.parseOperationType() p.expect(lexer.Colon) op.Type = p.parseName() return &op } -func (p *parser) parseScalarTypeDefinition(description string) *Definition { - p.expectKeyword("scalar") +func (p *parser) parseScalarTypeDefinition(description descriptionWithComment) *Definition { + _, comment := p.expectKeyword("scalar") var def Definition def.Position = p.peekPos() + def.BeforeDescriptionComment = description.comment + def.Description = description.text + def.AfterDescriptionComment = comment def.Kind = Scalar - def.Description = description def.Name = p.parseName() def.Directives = p.parseDirectives(true) return &def } -func (p *parser) parseObjectTypeDefinition(description string) *Definition { - p.expectKeyword("type") +func (p *parser) parseObjectTypeDefinition(description descriptionWithComment) *Definition { + _, comment := p.expectKeyword("type") var def Definition def.Position = p.peekPos() def.Kind = Object - def.Description = description + def.BeforeDescriptionComment = description.comment + def.Description = description.text + def.AfterDescriptionComment = comment def.Name = p.parseName() def.Interfaces = p.parseImplementsInterfaces() def.Directives = p.parseDirectives(true) - def.Fields = p.parseFieldsDefinition() + def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition() return &def } @@ -176,18 +221,26 @@ func (p *parser) parseImplementsInterfaces() []string { return types } -func (p *parser) parseFieldsDefinition() FieldList { +func (p *parser) parseFieldsDefinition() (FieldList, *CommentGroup) { var defs FieldList - p.some(lexer.BraceL, lexer.BraceR, func() { + comment := p.some(lexer.BraceL, lexer.BraceR, func() { defs = append(defs, p.parseFieldDefinition()) }) - return defs + return defs, comment } func (p *parser) parseFieldDefinition() *FieldDefinition { var def FieldDefinition def.Position = p.peekPos() - def.Description = p.parseDescription() + + desc := p.parseDescription() + if desc.text != "" { + def.BeforeDescriptionComment = desc.comment + def.Description = desc.text + } + + p.peek() // peek to set p.comment + def.AfterDescriptionComment = p.comment def.Name = p.parseName() def.Arguments = p.parseArgumentDefs() p.expect(lexer.Colon) @@ -208,7 +261,15 @@ func (p *parser) parseArgumentDefs() ArgumentDefinitionList { func (p *parser) parseArgumentDef() *ArgumentDefinition { var def ArgumentDefinition def.Position = p.peekPos() - def.Description = p.parseDescription() + + desc := p.parseDescription() + if desc.text != "" { + def.BeforeDescriptionComment = desc.comment + def.Description = desc.text + } + + p.peek() // peek to set p.comment + def.AfterDescriptionComment = p.comment def.Name = p.parseName() p.expect(lexer.Colon) def.Type = p.parseTypeReference() @@ -222,7 +283,15 @@ func (p *parser) parseArgumentDef() *ArgumentDefinition { func (p *parser) parseInputValueDef() *FieldDefinition { var def FieldDefinition def.Position = p.peekPos() - def.Description = p.parseDescription() + + desc := p.parseDescription() + if desc.text != "" { + def.BeforeDescriptionComment = desc.comment + def.Description = desc.text + } + + p.peek() // peek to set p.comment + def.AfterDescriptionComment = p.comment def.Name = p.parseName() p.expect(lexer.Colon) def.Type = p.parseTypeReference() @@ -233,27 +302,31 @@ func (p *parser) parseInputValueDef() *FieldDefinition { return &def } -func (p *parser) parseInterfaceTypeDefinition(description string) *Definition { - p.expectKeyword("interface") +func (p *parser) parseInterfaceTypeDefinition(description descriptionWithComment) *Definition { + _, comment := p.expectKeyword("interface") var def Definition def.Position = p.peekPos() def.Kind = Interface - def.Description = description + def.BeforeDescriptionComment = description.comment + def.Description = description.text + def.AfterDescriptionComment = comment def.Name = p.parseName() def.Interfaces = p.parseImplementsInterfaces() def.Directives = p.parseDirectives(true) - def.Fields = p.parseFieldsDefinition() + def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition() return &def } -func (p *parser) parseUnionTypeDefinition(description string) *Definition { - p.expectKeyword("union") +func (p *parser) parseUnionTypeDefinition(description descriptionWithComment) *Definition { + _, comment := p.expectKeyword("union") var def Definition def.Position = p.peekPos() def.Kind = Union - def.Description = description + def.BeforeDescriptionComment = description.comment + def.Description = description.text + def.AfterDescriptionComment = comment def.Name = p.parseName() def.Directives = p.parseDirectives(true) def.Types = p.parseUnionMemberTypes() @@ -274,87 +347,101 @@ func (p *parser) parseUnionMemberTypes() []string { return types } -func (p *parser) parseEnumTypeDefinition(description string) *Definition { - p.expectKeyword("enum") +func (p *parser) parseEnumTypeDefinition(description descriptionWithComment) *Definition { + _, comment := p.expectKeyword("enum") var def Definition def.Position = p.peekPos() def.Kind = Enum - def.Description = description + def.BeforeDescriptionComment = description.comment + def.Description = description.text + def.AfterDescriptionComment = comment def.Name = p.parseName() def.Directives = p.parseDirectives(true) - def.EnumValues = p.parseEnumValuesDefinition() + def.EnumValues, def.EndOfDefinitionComment = p.parseEnumValuesDefinition() return &def } -func (p *parser) parseEnumValuesDefinition() EnumValueList { +func (p *parser) parseEnumValuesDefinition() (EnumValueList, *CommentGroup) { var values EnumValueList - p.some(lexer.BraceL, lexer.BraceR, func() { + comment := p.some(lexer.BraceL, lexer.BraceR, func() { values = append(values, p.parseEnumValueDefinition()) }) - return values + return values, comment } func (p *parser) parseEnumValueDefinition() *EnumValueDefinition { - return &EnumValueDefinition{ - Position: p.peekPos(), - Description: p.parseDescription(), - Name: p.parseName(), - Directives: p.parseDirectives(true), + var def EnumValueDefinition + def.Position = p.peekPos() + desc := p.parseDescription() + if desc.text != "" { + def.BeforeDescriptionComment = desc.comment + def.Description = desc.text } + + p.peek() // peek to set p.comment + def.AfterDescriptionComment = p.comment + + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + + return &def } -func (p *parser) parseInputObjectTypeDefinition(description string) *Definition { - p.expectKeyword("input") +func (p *parser) parseInputObjectTypeDefinition(description descriptionWithComment) *Definition { + _, comment := p.expectKeyword("input") var def Definition def.Position = p.peekPos() def.Kind = InputObject - def.Description = description + def.BeforeDescriptionComment = description.comment + def.Description = description.text + def.AfterDescriptionComment = comment def.Name = p.parseName() def.Directives = p.parseDirectives(true) - def.Fields = p.parseInputFieldsDefinition() + def.Fields, def.EndOfDefinitionComment = p.parseInputFieldsDefinition() return &def } -func (p *parser) parseInputFieldsDefinition() FieldList { +func (p *parser) parseInputFieldsDefinition() (FieldList, *CommentGroup) { var values FieldList - p.some(lexer.BraceL, lexer.BraceR, func() { + comment := p.some(lexer.BraceL, lexer.BraceR, func() { values = append(values, p.parseInputValueDef()) }) - return values + return values, comment } func (p *parser) parseTypeSystemExtension(doc *SchemaDocument) { - p.expectKeyword("extend") + _, comment := p.expectKeyword("extend") switch p.peek().Value { case "schema": - doc.SchemaExtension = append(doc.SchemaExtension, p.parseSchemaExtension()) + doc.SchemaExtension = append(doc.SchemaExtension, p.parseSchemaExtension(comment)) case "scalar": - doc.Extensions = append(doc.Extensions, p.parseScalarTypeExtension()) + doc.Extensions = append(doc.Extensions, p.parseScalarTypeExtension(comment)) case "type": - doc.Extensions = append(doc.Extensions, p.parseObjectTypeExtension()) + doc.Extensions = append(doc.Extensions, p.parseObjectTypeExtension(comment)) case "interface": - doc.Extensions = append(doc.Extensions, p.parseInterfaceTypeExtension()) + doc.Extensions = append(doc.Extensions, p.parseInterfaceTypeExtension(comment)) case "union": - doc.Extensions = append(doc.Extensions, p.parseUnionTypeExtension()) + doc.Extensions = append(doc.Extensions, p.parseUnionTypeExtension(comment)) case "enum": - doc.Extensions = append(doc.Extensions, p.parseEnumTypeExtension()) + doc.Extensions = append(doc.Extensions, p.parseEnumTypeExtension(comment)) case "input": - doc.Extensions = append(doc.Extensions, p.parseInputObjectTypeExtension()) + doc.Extensions = append(doc.Extensions, p.parseInputObjectTypeExtension(comment)) default: p.unexpectedError() } } -func (p *parser) parseSchemaExtension() *SchemaDefinition { +func (p *parser) parseSchemaExtension(comment *CommentGroup) *SchemaDefinition { p.expectKeyword("schema") var def SchemaDefinition def.Position = p.peekPos() + def.AfterDescriptionComment = comment def.Directives = p.parseDirectives(true) - p.some(lexer.BraceL, lexer.BraceR, func() { + def.EndOfDefinitionComment = p.some(lexer.BraceL, lexer.BraceR, func() { def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition()) }) if len(def.Directives) == 0 && len(def.OperationTypes) == 0 { @@ -363,11 +450,12 @@ func (p *parser) parseSchemaExtension() *SchemaDefinition { return &def } -func (p *parser) parseScalarTypeExtension() *Definition { +func (p *parser) parseScalarTypeExtension(comment *CommentGroup) *Definition { p.expectKeyword("scalar") var def Definition def.Position = p.peekPos() + def.AfterDescriptionComment = comment def.Kind = Scalar def.Name = p.parseName() def.Directives = p.parseDirectives(true) @@ -377,42 +465,45 @@ func (p *parser) parseScalarTypeExtension() *Definition { return &def } -func (p *parser) parseObjectTypeExtension() *Definition { +func (p *parser) parseObjectTypeExtension(comment *CommentGroup) *Definition { p.expectKeyword("type") var def Definition def.Position = p.peekPos() + def.AfterDescriptionComment = comment def.Kind = Object def.Name = p.parseName() def.Interfaces = p.parseImplementsInterfaces() def.Directives = p.parseDirectives(true) - def.Fields = p.parseFieldsDefinition() + def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition() if len(def.Interfaces) == 0 && len(def.Directives) == 0 && len(def.Fields) == 0 { p.unexpectedError() } return &def } -func (p *parser) parseInterfaceTypeExtension() *Definition { +func (p *parser) parseInterfaceTypeExtension(comment *CommentGroup) *Definition { p.expectKeyword("interface") var def Definition def.Position = p.peekPos() + def.AfterDescriptionComment = comment def.Kind = Interface def.Name = p.parseName() def.Directives = p.parseDirectives(true) - def.Fields = p.parseFieldsDefinition() + def.Fields, def.EndOfDefinitionComment = p.parseFieldsDefinition() if len(def.Directives) == 0 && len(def.Fields) == 0 { p.unexpectedError() } return &def } -func (p *parser) parseUnionTypeExtension() *Definition { +func (p *parser) parseUnionTypeExtension(comment *CommentGroup) *Definition { p.expectKeyword("union") var def Definition def.Position = p.peekPos() + def.AfterDescriptionComment = comment def.Kind = Union def.Name = p.parseName() def.Directives = p.parseDirectives(true) @@ -424,43 +515,47 @@ func (p *parser) parseUnionTypeExtension() *Definition { return &def } -func (p *parser) parseEnumTypeExtension() *Definition { +func (p *parser) parseEnumTypeExtension(comment *CommentGroup) *Definition { p.expectKeyword("enum") var def Definition def.Position = p.peekPos() + def.AfterDescriptionComment = comment def.Kind = Enum def.Name = p.parseName() def.Directives = p.parseDirectives(true) - def.EnumValues = p.parseEnumValuesDefinition() + def.EnumValues, def.EndOfDefinitionComment = p.parseEnumValuesDefinition() if len(def.Directives) == 0 && len(def.EnumValues) == 0 { p.unexpectedError() } return &def } -func (p *parser) parseInputObjectTypeExtension() *Definition { +func (p *parser) parseInputObjectTypeExtension(comment *CommentGroup) *Definition { p.expectKeyword("input") var def Definition def.Position = p.peekPos() + def.AfterDescriptionComment = comment def.Kind = InputObject def.Name = p.parseName() def.Directives = p.parseDirectives(false) - def.Fields = p.parseInputFieldsDefinition() + def.Fields, def.EndOfDefinitionComment = p.parseInputFieldsDefinition() if len(def.Directives) == 0 && len(def.Fields) == 0 { p.unexpectedError() } return &def } -func (p *parser) parseDirectiveDefinition(description string) *DirectiveDefinition { - p.expectKeyword("directive") +func (p *parser) parseDirectiveDefinition(description descriptionWithComment) *DirectiveDefinition { + _, comment := p.expectKeyword("directive") p.expect(lexer.At) var def DirectiveDefinition def.Position = p.peekPos() - def.Description = description + def.BeforeDescriptionComment = description.comment + def.Description = description.text + def.AfterDescriptionComment = comment def.Name = p.parseName() def.Arguments = p.parseArgumentDefs() @@ -487,7 +582,7 @@ func (p *parser) parseDirectiveLocations() []DirectiveLocation { } func (p *parser) parseDirectiveLocation() DirectiveLocation { - name := p.expect(lexer.Name) + name, _ := p.expect(lexer.Name) switch name.Value { case `QUERY`: @@ -533,3 +628,8 @@ func (p *parser) parseDirectiveLocation() DirectiveLocation { p.unexpectedToken(name) return "" } + +type descriptionWithComment struct { + text string + comment *CommentGroup +} diff --git a/internal/gqlparser/parser/schema_test.yml b/vendor/github.com/vektah/gqlparser/v2/parser/schema_test.yml similarity index 81% rename from internal/gqlparser/parser/schema_test.yml rename to vendor/github.com/vektah/gqlparser/v2/parser/schema_test.yml index 8b6a5d0ca3..705514a995 100644 --- a/internal/gqlparser/parser/schema_test.yml +++ b/vendor/github.com/vektah/gqlparser/v2/parser/schema_test.yml @@ -15,6 +15,67 @@ object types: Name: "world" Type: String + - name: with comments + input: | + # Hello + # Hello another + type Hello { + # World + # World another + world: String + # end of type comments + } + # end of file comments + ast: | + + Definitions: [Definition] + - + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Fields: [FieldDefinition] + - + Name: "world" + Type: String + AfterDescriptionComment: "# World\n# World another\n" + AfterDescriptionComment: "# Hello\n# Hello another\n" + EndOfDefinitionComment: "# end of type comments\n" + Comment: "# end of file comments\n" + + - name: with comments and description + input: | + # Hello + # Hello another + "type description" + # Hello after description + # Hello after description another + type Hello { + # World + # World another + "field description" + # World after description + # World after description another + world: String + # end of definition coments + # end of definition comments another + } + ast: | + + Definitions: [Definition] + - + Kind: DefinitionKind("OBJECT") + Description: "type description" + Name: "Hello" + Fields: [FieldDefinition] + - + Description: "field description" + Name: "world" + Type: String + BeforeDescriptionComment: "# World\n# World another\n" + AfterDescriptionComment: "# World after description\n# World after description another\n" + BeforeDescriptionComment: "# Hello\n# Hello another\n" + AfterDescriptionComment: "# Hello after description\n# Hello after description another\n" + EndOfDefinitionComment: "# end of definition coments\n# end of definition comments another\n" + - name: with description input: | "Description" @@ -35,6 +96,7 @@ object types: - name: with block description input: | + # Before description comment """ Description """ @@ -53,6 +115,8 @@ object types: - Name: "world" Type: String + BeforeDescriptionComment: "# Before description comment\n" + AfterDescriptionComment: "# Even with comments between them\n" - name: with field arg input: | type Hello { @@ -146,8 +210,11 @@ object types: type extensions: - name: Object extension input: | + # comment extend type Hello { + # comment world world: String + # end of definition comment } ast: | @@ -159,6 +226,9 @@ type extensions: - Name: "world" Type: String + AfterDescriptionComment: "# comment world\n" + AfterDescriptionComment: "# comment\n" + EndOfDefinitionComment: "# end of definition comment\n" - name: without any fields input: "extend type Hello implements Greeting" @@ -277,6 +347,30 @@ schema definition: Operation: Operation("query") Type: "Query" + - name: with comments and description + input: | + # before description comment + "description" + # after description comment + schema { + # before field comment + query: Query + # after field comment + } + ast: | + + Schema: [SchemaDefinition] + - + Description: "description" + OperationTypes: [OperationTypeDefinition] + - + Operation: Operation("query") + Type: "Query" + Comment: "# before field comment\n" + BeforeDescriptionComment: "# before description comment\n" + AfterDescriptionComment: "# after description comment\n" + EndOfDefinitionComment: "# after field comment\n" + schema extensions: - name: simple input: | @@ -292,6 +386,26 @@ schema extensions: Operation: Operation("mutation") Type: "Mutation" + - name: with comment and description + input: | + # before extend comment + extend schema { + # before field comment + mutation: Mutation + # after field comment + } + ast: | + + SchemaExtension: [SchemaDefinition] + - + OperationTypes: [OperationTypeDefinition] + - + Operation: Operation("mutation") + Type: "Mutation" + Comment: "# before field comment\n" + AfterDescriptionComment: "# before extend comment\n" + EndOfDefinitionComment: "# after field comment\n" + - name: directive only input: "extend schema @directive" ast: | diff --git a/internal/gqlparser/validator/error.go b/vendor/github.com/vektah/gqlparser/v2/validator/error.go similarity index 91% rename from internal/gqlparser/validator/error.go rename to vendor/github.com/vektah/gqlparser/v2/validator/error.go index f31f180a2e..f8f76055ac 100644 --- a/internal/gqlparser/validator/error.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/error.go @@ -3,8 +3,8 @@ package validator import ( "fmt" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - "github.com/open-policy-agent/opa/internal/gqlparser/gqlerror" + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/gqlerror" ) type ErrorOption func(err *gqlerror.Error) diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/imported/prelude.graphql b/vendor/github.com/vektah/gqlparser/v2/validator/imported/prelude.graphql new file mode 100644 index 0000000000..8be3d2f5b6 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/imported/prelude.graphql @@ -0,0 +1,250 @@ +# This file defines all the implicitly declared types that are required by the graphql spec. It is implicitly included by calls to LoadSchema + +"The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1." +scalar Int + +"The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point)." +scalar Float + +"The `String`scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text." +scalar String + +"The `Boolean` scalar type represents `true` or `false`." +scalar Boolean + +"""The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID.""" +scalar ID + +"Directs the executor to defer this fragment when the `if` argument is true or undefined." +directive @defer( + "Deferred when true or undefined." + if: Boolean = true, + "Unique name" + label: String +) on FRAGMENT_SPREAD | INLINE_FRAGMENT + +""" +Directs the executor to include this field or fragment only when the `if` argument is true. +""" +directive @include( + """Included when true.""" + if: Boolean! +) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +""" +Directs the executor to skip this field or fragment when the `if` argument is true. +""" +directive @skip( + """Skipped when true.""" + if: Boolean! +) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +"""Marks an element of a GraphQL schema as no longer supported.""" +directive @deprecated( + """ + Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/). + """ + reason: String = "No longer supported" +) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE + +"""Exposes a URL that specifies the behavior of this scalar.""" +directive @specifiedBy( + """The URL that specifies the behavior of this scalar.""" + url: String! +) on SCALAR + +""" +Indicates exactly one field must be supplied and this field must not be `null`. +""" +directive @oneOf on INPUT_OBJECT + +""" +A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations. +""" +type __Schema { + description: String + + """A list of all types supported by this server.""" + types: [__Type!]! + + """The type that query operations will be rooted at.""" + queryType: __Type! + + """ + If this server supports mutation, the type that mutation operations will be rooted at. + """ + mutationType: __Type + + """ + If this server support subscription, the type that subscription operations will be rooted at. + """ + subscriptionType: __Type + + """A list of all directives supported by this server.""" + directives: [__Directive!]! +} + +""" +The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum. + +Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional `specifiedByURL`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types. +""" +type __Type { + kind: __TypeKind! + name: String + description: String + specifiedByURL: String + fields(includeDeprecated: Boolean = false): [__Field!] + interfaces: [__Type!] + possibleTypes: [__Type!] + enumValues(includeDeprecated: Boolean = false): [__EnumValue!] + inputFields(includeDeprecated: Boolean = false): [__InputValue!] + ofType: __Type + isOneOf: Boolean +} + +"""An enum describing what kind of type a given `__Type` is.""" +enum __TypeKind { + """Indicates this type is a scalar.""" + SCALAR + + """ + Indicates this type is an object. `fields` and `interfaces` are valid fields. + """ + OBJECT + + """ + Indicates this type is an interface. `fields`, `interfaces`, and `possibleTypes` are valid fields. + """ + INTERFACE + + """Indicates this type is a union. `possibleTypes` is a valid field.""" + UNION + + """Indicates this type is an enum. `enumValues` is a valid field.""" + ENUM + + """ + Indicates this type is an input object. `inputFields` is a valid field. + """ + INPUT_OBJECT + + """Indicates this type is a list. `ofType` is a valid field.""" + LIST + + """Indicates this type is a non-null. `ofType` is a valid field.""" + NON_NULL +} + +""" +Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type. +""" +type __Field { + name: String! + description: String + args(includeDeprecated: Boolean = false): [__InputValue!]! + type: __Type! + isDeprecated: Boolean! + deprecationReason: String +} + +""" +Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value. +""" +type __InputValue { + name: String! + description: String + type: __Type! + + """ + A GraphQL-formatted string representing the default value for this input value. + """ + defaultValue: String + isDeprecated: Boolean! + deprecationReason: String +} + +""" +One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string. +""" +type __EnumValue { + name: String! + description: String + isDeprecated: Boolean! + deprecationReason: String +} + +""" +A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document. + +In some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor. +""" +type __Directive { + name: String! + description: String + isRepeatable: Boolean! + locations: [__DirectiveLocation!]! + args(includeDeprecated: Boolean = false): [__InputValue!]! +} + +""" +A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies. +""" +enum __DirectiveLocation { + """Location adjacent to a query operation.""" + QUERY + + """Location adjacent to a mutation operation.""" + MUTATION + + """Location adjacent to a subscription operation.""" + SUBSCRIPTION + + """Location adjacent to a field.""" + FIELD + + """Location adjacent to a fragment definition.""" + FRAGMENT_DEFINITION + + """Location adjacent to a fragment spread.""" + FRAGMENT_SPREAD + + """Location adjacent to an inline fragment.""" + INLINE_FRAGMENT + + """Location adjacent to a variable definition.""" + VARIABLE_DEFINITION + + """Location adjacent to a schema definition.""" + SCHEMA + + """Location adjacent to a scalar definition.""" + SCALAR + + """Location adjacent to an object type definition.""" + OBJECT + + """Location adjacent to a field definition.""" + FIELD_DEFINITION + + """Location adjacent to an argument definition.""" + ARGUMENT_DEFINITION + + """Location adjacent to an interface definition.""" + INTERFACE + + """Location adjacent to a union definition.""" + UNION + + """Location adjacent to an enum definition.""" + ENUM + + """Location adjacent to an enum value definition.""" + ENUM_VALUE + + """Location adjacent to an input object type definition.""" + INPUT_OBJECT + + """Location adjacent to an input object field definition.""" + INPUT_FIELD_DEFINITION +} \ No newline at end of file diff --git a/internal/gqlparser/validator/messaging.go b/vendor/github.com/vektah/gqlparser/v2/validator/messaging.go similarity index 100% rename from internal/gqlparser/validator/messaging.go rename to vendor/github.com/vektah/gqlparser/v2/validator/messaging.go diff --git a/internal/gqlparser/validator/prelude.go b/vendor/github.com/vektah/gqlparser/v2/validator/prelude.go similarity index 66% rename from internal/gqlparser/validator/prelude.go rename to vendor/github.com/vektah/gqlparser/v2/validator/prelude.go index 86796fab6c..5c88e93b3f 100644 --- a/internal/gqlparser/validator/prelude.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/prelude.go @@ -3,10 +3,10 @@ package validator import ( _ "embed" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" ) -//go:embed prelude.graphql +//go:embed imported/prelude.graphql var preludeGraphql string var Prelude = &ast.Source{ diff --git a/internal/gqlparser/validator/rules/fields_on_correct_type.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/fields_on_correct_type.go similarity index 56% rename from internal/gqlparser/validator/rules/fields_on_correct_type.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/fields_on_correct_type.go index f681767475..b57d2a9014 100644 --- a/internal/gqlparser/validator/rules/fields_on_correct_type.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/fields_on_correct_type.go @@ -1,40 +1,58 @@ -package validator +package rules import ( "fmt" "sort" "strings" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("FieldsOnCorrectType", func(observers *Events, addError AddErrFunc) { - observers.OnField(func(walker *Walker, field *ast.Field) { - if field.ObjectDefinition == nil || field.Definition != nil { - return - } +func ruleFuncFieldsOnCorrectType(observers *Events, addError AddErrFunc, disableSuggestion bool) { + observers.OnField(func(walker *Walker, field *ast.Field) { + if field.ObjectDefinition == nil || field.Definition != nil { + return + } - message := fmt.Sprintf(`Cannot query field "%s" on type "%s".`, field.Name, field.ObjectDefinition.Name) + message := fmt.Sprintf(`Cannot query field "%s" on type "%s".`, field.Name, field.ObjectDefinition.Name) + if !disableSuggestion { if suggestedTypeNames := getSuggestedTypeNames(walker, field.ObjectDefinition, field.Name); suggestedTypeNames != nil { message += " Did you mean to use an inline fragment on " + QuotedOrList(suggestedTypeNames...) + "?" } else if suggestedFieldNames := getSuggestedFieldNames(field.ObjectDefinition, field.Name); suggestedFieldNames != nil { message += " Did you mean " + QuotedOrList(suggestedFieldNames...) + "?" } + } - addError( - Message(message), //nolint:govet - At(field.Position), - ) - }) + addError( + Message("%s", message), + At(field.Position), + ) }) } -// Go through all of the implementations of type, as well as the interfaces +var FieldsOnCorrectTypeRule = Rule{ + Name: "FieldsOnCorrectType", + RuleFunc: func(observers *Events, addError AddErrFunc) { + ruleFuncFieldsOnCorrectType(observers, addError, false) + }, +} + +var FieldsOnCorrectTypeRuleWithoutSuggestions = Rule{ + Name: "FieldsOnCorrectTypeWithoutSuggestions", + RuleFunc: func(observers *Events, addError AddErrFunc) { + ruleFuncFieldsOnCorrectType(observers, addError, true) + }, +} + +func init() { + AddRule(FieldsOnCorrectTypeRule.Name, FieldsOnCorrectTypeRule.RuleFunc) +} + +// Go through all the implementations of type, as well as the interfaces // that they implement. If any of those types include the provided field, // suggest them, sorted by how often the type is referenced, starting // with Interfaces. @@ -44,7 +62,7 @@ func getSuggestedTypeNames(walker *Walker, parent *ast.Definition, name string) } possibleTypes := walker.Schema.GetPossibleTypes(parent) - var suggestedObjectTypes = make([]string, 0, len(possibleTypes)) + suggestedObjectTypes := make([]string, 0, len(possibleTypes)) var suggestedInterfaceTypes []string interfaceUsageCount := map[string]int{} @@ -67,7 +85,7 @@ func getSuggestedTypeNames(walker *Walker, parent *ast.Definition, name string) } } - suggestedTypes := append(suggestedInterfaceTypes, suggestedObjectTypes...) + suggestedTypes := concatSlice(suggestedInterfaceTypes, suggestedObjectTypes) sort.SliceStable(suggestedTypes, func(i, j int) bool { typeA, typeB := suggestedTypes[i], suggestedTypes[j] @@ -81,6 +99,16 @@ func getSuggestedTypeNames(walker *Walker, parent *ast.Definition, name string) return suggestedTypes } +// By employing a full slice expression (slice[low:high:max]), +// where max is set to the slice’s length, +// we ensure that appending elements results +// in a slice backed by a distinct array. +// This method prevents the shared array issue +func concatSlice(first []string, second []string) []string { + n := len(first) + return append(first[:n:n], second...) +} + // For the field name provided, determine if there are any similar field names // that may be the result of a typo. func getSuggestedFieldNames(parent *ast.Definition, name string) []string { @@ -88,7 +116,7 @@ func getSuggestedFieldNames(parent *ast.Definition, name string) []string { return nil } - var possibleFieldNames = make([]string, 0, len(parent.Fields)) + possibleFieldNames := make([]string, 0, len(parent.Fields)) for _, field := range parent.Fields { possibleFieldNames = append(possibleFieldNames, field.Name) } diff --git a/internal/gqlparser/validator/rules/fragments_on_composite_types.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/fragments_on_composite_types.go similarity index 58% rename from internal/gqlparser/validator/rules/fragments_on_composite_types.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/fragments_on_composite_types.go index 861e3b16cf..a88e3f1cf7 100644 --- a/internal/gqlparser/validator/rules/fragments_on_composite_types.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/fragments_on_composite_types.go @@ -1,16 +1,17 @@ -package validator +package rules import ( "fmt" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("FragmentsOnCompositeTypes", func(observers *Events, addError AddErrFunc) { +var FragmentsOnCompositeTypesRule = Rule{ + Name: "FragmentsOnCompositeTypes", + RuleFunc: func(observers *Events, addError AddErrFunc) { observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) { fragmentType := walker.Schema.Types[inlineFragment.TypeCondition] if fragmentType == nil || fragmentType.IsCompositeType() { @@ -20,12 +21,12 @@ func init() { message := fmt.Sprintf(`Fragment cannot condition on non composite type "%s".`, inlineFragment.TypeCondition) addError( - Message(message), //nolint:govet + Message("%s", message), At(inlineFragment.Position), ) }) - observers.OnFragment(func(_ *Walker, fragment *ast.FragmentDefinition) { + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { if fragment.Definition == nil || fragment.TypeCondition == "" || fragment.Definition.IsCompositeType() { return } @@ -33,9 +34,13 @@ func init() { message := fmt.Sprintf(`Fragment "%s" cannot condition on non composite type "%s".`, fragment.Name, fragment.TypeCondition) addError( - Message(message), //nolint:govet + Message("%s", message), At(fragment.Position), ) }) - }) + }, +} + +func init() { + AddRule(FragmentsOnCompositeTypesRule.Name, FragmentsOnCompositeTypesRule.RuleFunc) } diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_argument_names.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_argument_names.go new file mode 100644 index 0000000000..83b4e05758 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_argument_names.go @@ -0,0 +1,88 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +func ruleFuncKnownArgumentNames(observers *Events, addError AddErrFunc, disableSuggestion bool) { + // A GraphQL field is only valid if all supplied arguments are defined by that field. + observers.OnField(func(walker *Walker, field *ast.Field) { + if field.Definition == nil || field.ObjectDefinition == nil { + return + } + for _, arg := range field.Arguments { + def := field.Definition.Arguments.ForName(arg.Name) + if def != nil { + continue + } + + if disableSuggestion { + addError( + Message(`Unknown argument "%s" on field "%s.%s".`, arg.Name, field.ObjectDefinition.Name, field.Name), + At(field.Position), + ) + } else { + var suggestions []string + for _, argDef := range field.Definition.Arguments { + suggestions = append(suggestions, argDef.Name) + } + addError( + Message(`Unknown argument "%s" on field "%s.%s".`, arg.Name, field.ObjectDefinition.Name, field.Name), + SuggestListQuoted("Did you mean", arg.Name, suggestions), + At(field.Position), + ) + } + } + }) + + observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + if directive.Definition == nil { + return + } + for _, arg := range directive.Arguments { + def := directive.Definition.Arguments.ForName(arg.Name) + if def != nil { + continue + } + + if disableSuggestion { + addError( + Message(`Unknown argument "%s" on directive "@%s".`, arg.Name, directive.Name), + At(directive.Position), + ) + } else { + var suggestions []string + for _, argDef := range directive.Definition.Arguments { + suggestions = append(suggestions, argDef.Name) + } + + addError( + Message(`Unknown argument "%s" on directive "@%s".`, arg.Name, directive.Name), + SuggestListQuoted("Did you mean", arg.Name, suggestions), + At(directive.Position), + ) + } + } + }) +} + +var KnownArgumentNamesRule = Rule{ + Name: "KnownArgumentNames", + RuleFunc: func(observers *Events, addError AddErrFunc) { + ruleFuncKnownArgumentNames(observers, addError, false) + }, +} + +var KnownArgumentNamesRuleWithoutSuggestions = Rule{ + Name: "KnownArgumentNamesWithoutSuggestions", + RuleFunc: func(observers *Events, addError AddErrFunc) { + ruleFuncKnownArgumentNames(observers, addError, true) + }, +} + +func init() { + AddRule(KnownArgumentNamesRule.Name, KnownArgumentNamesRule.RuleFunc) +} diff --git a/internal/gqlparser/validator/rules/known_directives.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_directives.go similarity index 62% rename from internal/gqlparser/validator/rules/known_directives.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/known_directives.go index 9855291e3b..ccb5efeb95 100644 --- a/internal/gqlparser/validator/rules/known_directives.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_directives.go @@ -1,21 +1,22 @@ -package validator +package rules import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("KnownDirectives", func(observers *Events, addError AddErrFunc) { +var KnownDirectivesRule = Rule{ + Name: "KnownDirectives", + RuleFunc: func(observers *Events, addError AddErrFunc) { type mayNotBeUsedDirective struct { Name string Line int Column int } - var seen = map[mayNotBeUsedDirective]bool{} - observers.OnDirective(func(_ *Walker, directive *ast.Directive) { + seen := map[mayNotBeUsedDirective]bool{} + observers.OnDirective(func(walker *Walker, directive *ast.Directive) { if directive.Definition == nil { addError( Message(`Unknown directive "@%s".`, directive.Name), @@ -45,5 +46,9 @@ func init() { seen[tmp] = true } }) - }) + }, +} + +func init() { + AddRule(KnownDirectivesRule.Name, KnownDirectivesRule.RuleFunc) } diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_fragment_names.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_fragment_names.go new file mode 100644 index 0000000000..525698fb94 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_fragment_names.go @@ -0,0 +1,26 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var KnownFragmentNamesRule = Rule{ + Name: "KnownFragmentNames", + RuleFunc: func(observers *Events, addError AddErrFunc) { + observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) { + if fragmentSpread.Definition == nil { + addError( + Message(`Unknown fragment "%s".`, fragmentSpread.Name), + At(fragmentSpread.Position), + ) + } + }) + }, +} + +func init() { + AddRule(KnownFragmentNamesRule.Name, KnownFragmentNamesRule.RuleFunc) +} diff --git a/internal/gqlparser/validator/rules/known_root_type.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_root_type.go similarity index 69% rename from internal/gqlparser/validator/rules/known_root_type.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/known_root_type.go index ab97cd9017..aa66d16c28 100644 --- a/internal/gqlparser/validator/rules/known_root_type.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_root_type.go @@ -1,16 +1,17 @@ -package validator +package rules import ( "fmt" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("KnownRootType", func(observers *Events, addError AddErrFunc) { +var KnownRootTypeRule = Rule{ + Name: "KnownRootType", + RuleFunc: func(observers *Events, addError AddErrFunc) { // A query's root must be a valid type. Surprisingly, this isn't // checked anywhere else! observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { @@ -33,5 +34,9 @@ func init() { At(operation.Position)) } }) - }) + }, +} + +func init() { + AddRule(KnownRootTypeRule.Name, KnownRootTypeRule.RuleFunc) } diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_type_names.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_type_names.go new file mode 100644 index 0000000000..ef85c58e65 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/known_type_names.go @@ -0,0 +1,84 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +func ruleFuncKnownTypeNames(observers *Events, addError AddErrFunc, disableSuggestion bool) { + observers.OnVariable(func(walker *Walker, variable *ast.VariableDefinition) { + typeName := variable.Type.Name() + typdef := walker.Schema.Types[typeName] + if typdef != nil { + return + } + + addError( + Message(`Unknown type "%s".`, typeName), + At(variable.Position), + ) + }) + + observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) { + typedName := inlineFragment.TypeCondition + if typedName == "" { + return + } + + def := walker.Schema.Types[typedName] + if def != nil { + return + } + + addError( + Message(`Unknown type "%s".`, typedName), + At(inlineFragment.Position), + ) + }) + + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + typeName := fragment.TypeCondition + def := walker.Schema.Types[typeName] + if def != nil { + return + } + + if disableSuggestion { + addError( + Message(`Unknown type "%s".`, typeName), + At(fragment.Position), + ) + } else { + var possibleTypes []string + for _, t := range walker.Schema.Types { + possibleTypes = append(possibleTypes, t.Name) + } + + addError( + Message(`Unknown type "%s".`, typeName), + SuggestListQuoted("Did you mean", typeName, possibleTypes), + At(fragment.Position), + ) + } + }) +} + +var KnownTypeNamesRule = Rule{ + Name: "KnownTypeNames", + RuleFunc: func(observers *Events, addError AddErrFunc) { + ruleFuncKnownTypeNames(observers, addError, false) + }, +} + +var KnownTypeNamesRuleWithoutSuggestions = Rule{ + Name: "KnownTypeNamesWithoutSuggestions", + RuleFunc: func(observers *Events, addError AddErrFunc) { + ruleFuncKnownTypeNames(observers, addError, true) + }, +} + +func init() { + AddRule(KnownTypeNamesRule.Name, KnownTypeNamesRule.RuleFunc) +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/lone_anonymous_operation.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/lone_anonymous_operation.go new file mode 100644 index 0000000000..6e246f715f --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/lone_anonymous_operation.go @@ -0,0 +1,26 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var LoneAnonymousOperationRule = Rule{ + Name: "LoneAnonymousOperation", + RuleFunc: func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + if operation.Name == "" && len(walker.Document.Operations) > 1 { + addError( + Message(`This anonymous operation must be the only defined operation.`), + At(operation.Position), + ) + } + }) + }, +} + +func init() { + AddRule(LoneAnonymousOperationRule.Name, LoneAnonymousOperationRule.RuleFunc) +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/max_introspection_depth.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/max_introspection_depth.go new file mode 100644 index 0000000000..57a68b32b9 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/max_introspection_depth.go @@ -0,0 +1,90 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +const maxListsDepth = 3 + +var MaxIntrospectionDepth = Rule{ + Name: "MaxIntrospectionDepth", + RuleFunc: func(observers *Events, addError AddErrFunc) { + // Counts the depth of list fields in "__Type" recursively and + // returns `true` if the limit has been reached. + observers.OnField(func(walker *Walker, field *ast.Field) { + if field.Name == "__schema" || field.Name == "__type" { + visitedFragments := make(map[string]bool) + if checkDepthField(field, visitedFragments, 0) { + addError( + Message(`Maximum introspection depth exceeded`), + At(field.Position), + ) + } + return + } + }) + }, +} + +func checkDepthSelectionSet(selectionSet ast.SelectionSet, visitedFragments map[string]bool, depth int) bool { + for _, child := range selectionSet { + if field, ok := child.(*ast.Field); ok { + if checkDepthField(field, visitedFragments, depth) { + return true + } + } + if fragmentSpread, ok := child.(*ast.FragmentSpread); ok { + if checkDepthFragmentSpread(fragmentSpread, visitedFragments, depth) { + return true + } + } + if inlineFragment, ok := child.(*ast.InlineFragment); ok { + if checkDepthSelectionSet(inlineFragment.SelectionSet, visitedFragments, depth) { + return true + } + } + } + return false +} + +func checkDepthField(field *ast.Field, visitedFragments map[string]bool, depth int) bool { + if field.Name == "fields" || + field.Name == "interfaces" || + field.Name == "possibleTypes" || + field.Name == "inputFields" { + depth++ + if depth >= maxListsDepth { + return true + } + } + return checkDepthSelectionSet(field.SelectionSet, visitedFragments, depth) +} + +func checkDepthFragmentSpread(fragmentSpread *ast.FragmentSpread, visitedFragments map[string]bool, depth int) bool { + fragmentName := fragmentSpread.Name + if visited, ok := visitedFragments[fragmentName]; ok && visited { + // Fragment cycles are handled by `NoFragmentCyclesRule`. + return false + } + fragment := fragmentSpread.Definition + if fragment == nil { + // Missing fragments checks are handled by `KnownFragmentNamesRule`. + return false + } + + // Rather than following an immutable programming pattern which has + // significant memory and garbage collection overhead, we've opted to + // take a mutable approach for efficiency's sake. Importantly visiting a + // fragment twice is fine, so long as you don't do one visit inside the + // other. + visitedFragments[fragmentName] = true + defer delete(visitedFragments, fragmentName) + return checkDepthSelectionSet(fragment.SelectionSet, visitedFragments, depth) +} + +func init() { + AddRule(MaxIntrospectionDepth.Name, MaxIntrospectionDepth.RuleFunc) +} diff --git a/internal/gqlparser/validator/rules/no_fragment_cycles.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_fragment_cycles.go similarity index 83% rename from internal/gqlparser/validator/rules/no_fragment_cycles.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/no_fragment_cycles.go index edc562ddd4..4e7907e243 100644 --- a/internal/gqlparser/validator/rules/no_fragment_cycles.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_fragment_cycles.go @@ -1,17 +1,18 @@ -package validator +package rules import ( "fmt" "strings" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("NoFragmentCycles", func(observers *Events, addError AddErrFunc) { +var NoFragmentCyclesRule = Rule{ + Name: "NoFragmentCycles", + RuleFunc: func(observers *Events, addError AddErrFunc) { visitedFrags := make(map[string]bool) observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { @@ -51,7 +52,7 @@ func init() { } var via string if len(fragmentNames) != 0 { - via = " via " + strings.Join(fragmentNames, ", ") + via = fmt.Sprintf(" via %s", strings.Join(fragmentNames, ", ")) } addError( Message(`Cannot spread fragment "%s" within itself%s.`, spreadName, via), @@ -67,7 +68,11 @@ func init() { recursive(fragment) }) - }) + }, +} + +func init() { + AddRule(NoFragmentCyclesRule.Name, NoFragmentCyclesRule.RuleFunc) } func getFragmentSpreads(node ast.SelectionSet) []*ast.FragmentSpread { diff --git a/internal/gqlparser/validator/rules/no_undefined_variables.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_undefined_variables.go similarity index 57% rename from internal/gqlparser/validator/rules/no_undefined_variables.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/no_undefined_variables.go index e45a5e3d51..64f2dc7764 100644 --- a/internal/gqlparser/validator/rules/no_undefined_variables.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_undefined_variables.go @@ -1,14 +1,15 @@ -package validator +package rules import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("NoUndefinedVariables", func(observers *Events, addError AddErrFunc) { +var NoUndefinedVariablesRule = Rule{ + Name: "NoUndefinedVariables", + RuleFunc: func(observers *Events, addError AddErrFunc) { observers.OnValue(func(walker *Walker, value *ast.Value) { if walker.CurrentOperation == nil || value.Kind != ast.Variable || value.VariableDefinition != nil { return @@ -26,5 +27,9 @@ func init() { ) } }) - }) + }, +} + +func init() { + AddRule(NoUndefinedVariablesRule.Name, NoUndefinedVariablesRule.RuleFunc) } diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_unused_fragments.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_unused_fragments.go new file mode 100644 index 0000000000..a914ee6d34 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_unused_fragments.go @@ -0,0 +1,36 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var NoUnusedFragmentsRule = Rule{ + Name: "NoUnusedFragments", + RuleFunc: func(observers *Events, addError AddErrFunc) { + inFragmentDefinition := false + fragmentNameUsed := make(map[string]bool) + + observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) { + if !inFragmentDefinition { + fragmentNameUsed[fragmentSpread.Name] = true + } + }) + + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + inFragmentDefinition = true + if !fragmentNameUsed[fragment.Name] { + addError( + Message(`Fragment "%s" is never used.`, fragment.Name), + At(fragment.Position), + ) + } + }) + }, +} + +func init() { + AddRule(NoUnusedFragmentsRule.Name, NoUnusedFragmentsRule.RuleFunc) +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_unused_variables.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_unused_variables.go new file mode 100644 index 0000000000..daed80ebbb --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/no_unused_variables.go @@ -0,0 +1,37 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var NoUnusedVariablesRule = Rule{ + Name: "NoUnusedVariables", + RuleFunc: func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + for _, varDef := range operation.VariableDefinitions { + if varDef.Used { + continue + } + + if operation.Name != "" { + addError( + Message(`Variable "$%s" is never used in operation "%s".`, varDef.Variable, operation.Name), + At(varDef.Position), + ) + } else { + addError( + Message(`Variable "$%s" is never used.`, varDef.Variable), + At(varDef.Position), + ) + } + } + }) + }, +} + +func init() { + AddRule(NoUnusedVariablesRule.Name, NoUnusedVariablesRule.RuleFunc) +} diff --git a/internal/gqlparser/validator/rules/overlapping_fields_can_be_merged.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/overlapping_fields_can_be_merged.go similarity index 97% rename from internal/gqlparser/validator/rules/overlapping_fields_can_be_merged.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/overlapping_fields_can_be_merged.go index 1e207a43e7..1295682200 100644 --- a/internal/gqlparser/validator/rules/overlapping_fields_can_be_merged.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/overlapping_fields_can_be_merged.go @@ -1,19 +1,19 @@ -package validator +package rules import ( "bytes" "fmt" "reflect" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - - AddRule("OverlappingFieldsCanBeMerged", func(observers *Events, addError AddErrFunc) { +var OverlappingFieldsCanBeMergedRule = Rule{ + Name: "OverlappingFieldsCanBeMerged", + RuleFunc: func(observers *Events, addError AddErrFunc) { /** * Algorithm: * @@ -105,7 +105,11 @@ func init() { conflict.addFieldsConflictMessage(addError) } }) - }) + }, +} + +func init() { + AddRule(OverlappingFieldsCanBeMergedRule.Name, OverlappingFieldsCanBeMergedRule.RuleFunc) } type pairSet struct { @@ -304,10 +308,8 @@ func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFieldsAndFr } func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFragments(conflicts *conflictMessageContainer, areMutuallyExclusive bool, fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) { - var check func(fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) check = func(fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) { - if fragmentSpreadA.Name == fragmentSpreadB.Name { return } diff --git a/internal/gqlparser/validator/rules/possible_fragment_spreads.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/possible_fragment_spreads.go similarity index 82% rename from internal/gqlparser/validator/rules/possible_fragment_spreads.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/possible_fragment_spreads.go index 79cb20c49c..b81f375658 100644 --- a/internal/gqlparser/validator/rules/possible_fragment_spreads.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/possible_fragment_spreads.go @@ -1,15 +1,15 @@ -package validator +package rules import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("PossibleFragmentSpreads", func(observers *Events, addError AddErrFunc) { - +var PossibleFragmentSpreadsRule = Rule{ + Name: "PossibleFragmentSpreads", + RuleFunc: func(observers *Events, addError AddErrFunc) { validate := func(walker *Walker, parentDef *ast.Definition, fragmentName string, emitError func()) { if parentDef == nil { return @@ -66,5 +66,9 @@ func init() { ) }) }) - }) + }, +} + +func init() { + AddRule(PossibleFragmentSpreadsRule.Name, PossibleFragmentSpreadsRule.RuleFunc) } diff --git a/internal/gqlparser/validator/rules/provided_required_arguments.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/provided_required_arguments.go similarity index 67% rename from internal/gqlparser/validator/rules/provided_required_arguments.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/provided_required_arguments.go index d6d12c4fd2..90667af23b 100644 --- a/internal/gqlparser/validator/rules/provided_required_arguments.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/provided_required_arguments.go @@ -1,15 +1,15 @@ -package validator +package rules import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + "github.com/vektah/gqlparser/v2/ast" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("ProvidedRequiredArguments", func(observers *Events, addError AddErrFunc) { - observers.OnField(func(_ *Walker, field *ast.Field) { +var ProvidedRequiredArgumentsRule = Rule{ + Name: "ProvidedRequiredArguments", + RuleFunc: func(observers *Events, addError AddErrFunc) { + observers.OnField(func(walker *Walker, field *ast.Field) { if field.Definition == nil { return } @@ -35,7 +35,7 @@ func init() { } }) - observers.OnDirective(func(_ *Walker, directive *ast.Directive) { + observers.OnDirective(func(walker *Walker, directive *ast.Directive) { if directive.Definition == nil { return } @@ -60,5 +60,9 @@ func init() { ) } }) - }) + }, +} + +func init() { + AddRule(ProvidedRequiredArgumentsRule.Name, ProvidedRequiredArgumentsRule.RuleFunc) } diff --git a/internal/gqlparser/validator/rules/scalar_leafs.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/scalar_leafs.go similarity index 68% rename from internal/gqlparser/validator/rules/scalar_leafs.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/scalar_leafs.go index cd17b47c87..73a1e89677 100644 --- a/internal/gqlparser/validator/rules/scalar_leafs.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/scalar_leafs.go @@ -1,14 +1,15 @@ -package validator +package rules import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("ScalarLeafs", func(observers *Events, addError AddErrFunc) { +var ScalarLeafsRule = Rule{ + Name: "ScalarLeafs", + RuleFunc: func(observers *Events, addError AddErrFunc) { observers.OnField(func(walker *Walker, field *ast.Field) { if field.Definition == nil { return @@ -34,5 +35,9 @@ func init() { ) } }) - }) + }, +} + +func init() { + AddRule(ScalarLeafsRule.Name, ScalarLeafsRule.RuleFunc) } diff --git a/internal/gqlparser/validator/rules/single_field_subscriptions.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/single_field_subscriptions.go similarity index 82% rename from internal/gqlparser/validator/rules/single_field_subscriptions.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/single_field_subscriptions.go index 98cb984b40..1498d82986 100644 --- a/internal/gqlparser/validator/rules/single_field_subscriptions.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/single_field_subscriptions.go @@ -1,17 +1,18 @@ -package validator +package rules import ( "strconv" "strings" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("SingleFieldSubscriptions", func(observers *Events, addError AddErrFunc) { +var SingleFieldSubscriptionsRule = Rule{ + Name: "SingleFieldSubscriptions", + RuleFunc: func(observers *Events, addError AddErrFunc) { observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { if walker.Schema.Subscription == nil || operation.Operation != ast.Subscription { return @@ -40,7 +41,11 @@ func init() { } } }) - }) + }, +} + +func init() { + AddRule(SingleFieldSubscriptionsRule.Name, SingleFieldSubscriptionsRule.RuleFunc) } type topField struct { diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_argument_names.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_argument_names.go new file mode 100644 index 0000000000..b90cc65107 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_argument_names.go @@ -0,0 +1,40 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var UniqueArgumentNamesRule = Rule{ + Name: "UniqueArgumentNames", + RuleFunc: func(observers *Events, addError AddErrFunc) { + observers.OnField(func(walker *Walker, field *ast.Field) { + checkUniqueArgs(field.Arguments, addError) + }) + + observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + checkUniqueArgs(directive.Arguments, addError) + }) + }, +} + +func init() { + AddRule(UniqueArgumentNamesRule.Name, UniqueArgumentNamesRule.RuleFunc) +} + +func checkUniqueArgs(args ast.ArgumentList, addError AddErrFunc) { + knownArgNames := map[string]int{} + + for _, arg := range args { + if knownArgNames[arg.Name] == 1 { + addError( + Message(`There can be only one argument named "%s".`, arg.Name), + At(arg.Position), + ) + } + + knownArgNames[arg.Name]++ + } +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_directives_per_location.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_directives_per_location.go new file mode 100644 index 0000000000..4222f36aec --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_directives_per_location.go @@ -0,0 +1,31 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var UniqueDirectivesPerLocationRule = Rule{ + Name: "UniqueDirectivesPerLocation", + RuleFunc: func(observers *Events, addError AddErrFunc) { + observers.OnDirectiveList(func(walker *Walker, directives []*ast.Directive) { + seen := map[string]bool{} + + for _, dir := range directives { + if dir.Name != "repeatable" && seen[dir.Name] { + addError( + Message(`The directive "@%s" can only be used once at this location.`, dir.Name), + At(dir.Position), + ) + } + seen[dir.Name] = true + } + }) + }, +} + +func init() { + AddRule(UniqueDirectivesPerLocationRule.Name, UniqueDirectivesPerLocationRule.RuleFunc) +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_fragment_names.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_fragment_names.go new file mode 100644 index 0000000000..aab8eeb4eb --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_fragment_names.go @@ -0,0 +1,29 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var UniqueFragmentNamesRule = Rule{ + Name: "UniqueFragmentNames", + RuleFunc: func(observers *Events, addError AddErrFunc) { + seenFragments := map[string]bool{} + + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + if seenFragments[fragment.Name] { + addError( + Message(`There can be only one fragment named "%s".`, fragment.Name), + At(fragment.Position), + ) + } + seenFragments[fragment.Name] = true + }) + }, +} + +func init() { + AddRule(UniqueFragmentNamesRule.Name, UniqueFragmentNamesRule.RuleFunc) +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_input_field_names.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_input_field_names.go new file mode 100644 index 0000000000..250849344b --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_input_field_names.go @@ -0,0 +1,34 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var UniqueInputFieldNamesRule = Rule{ + Name: "UniqueInputFieldNames", + RuleFunc: func(observers *Events, addError AddErrFunc) { + observers.OnValue(func(walker *Walker, value *ast.Value) { + if value.Kind != ast.ObjectValue { + return + } + + seen := map[string]bool{} + for _, field := range value.Children { + if seen[field.Name] { + addError( + Message(`There can be only one input field named "%s".`, field.Name), + At(field.Position), + ) + } + seen[field.Name] = true + } + }) + }, +} + +func init() { + AddRule(UniqueInputFieldNamesRule.Name, UniqueInputFieldNamesRule.RuleFunc) +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_operation_names.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_operation_names.go new file mode 100644 index 0000000000..6f1ec26abf --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_operation_names.go @@ -0,0 +1,29 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var UniqueOperationNamesRule = Rule{ + Name: "UniqueOperationNames", + RuleFunc: func(observers *Events, addError AddErrFunc) { + seen := map[string]bool{} + + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + if seen[operation.Name] { + addError( + Message(`There can be only one operation named "%s".`, operation.Name), + At(operation.Position), + ) + } + seen[operation.Name] = true + }) + }, +} + +func init() { + AddRule(UniqueOperationNamesRule.Name, UniqueOperationNamesRule.RuleFunc) +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_variable_names.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_variable_names.go new file mode 100644 index 0000000000..6b037ed527 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/unique_variable_names.go @@ -0,0 +1,31 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var UniqueVariableNamesRule = Rule{ + Name: "UniqueVariableNames", + RuleFunc: func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + seen := map[string]int{} + for _, def := range operation.VariableDefinitions { + // add the same error only once per a variable. + if seen[def.Variable] == 1 { + addError( + Message(`There can be only one variable named "$%s".`, def.Variable), + At(def.Position), + ) + } + seen[def.Variable]++ + } + }) + }, +} + +func init() { + AddRule(UniqueVariableNamesRule.Name, UniqueVariableNamesRule.RuleFunc) +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/values_of_correct_type.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/values_of_correct_type.go new file mode 100644 index 0000000000..01510b7b5a --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/values_of_correct_type.go @@ -0,0 +1,250 @@ +package rules + +import ( + "errors" + "fmt" + "strconv" + + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +func ruleFuncValuesOfCorrectType(observers *Events, addError AddErrFunc, disableSuggestion bool) { + observers.OnValue(func(walker *Walker, value *ast.Value) { + if value.Definition == nil || value.ExpectedType == nil { + return + } + + if value.Kind == ast.NullValue && value.ExpectedType.NonNull { + addError( + Message(`Expected value of type "%s", found %s.`, value.ExpectedType.String(), value.String()), + At(value.Position), + ) + } + + if value.Definition.Kind == ast.Scalar { + // Skip custom validating scalars + if !value.Definition.OneOf("Int", "Float", "String", "Boolean", "ID") { + return + } + } + + var possibleEnums []string + if value.Definition.Kind == ast.Enum { + for _, val := range value.Definition.EnumValues { + possibleEnums = append(possibleEnums, val.Name) + } + } + + rawVal, err := value.Value(nil) + if err != nil { + unexpectedTypeMessage(addError, value) + } + + switch value.Kind { + case ast.NullValue: + return + case ast.ListValue: + if value.ExpectedType.Elem == nil { + unexpectedTypeMessage(addError, value) + return + } + + case ast.IntValue: + if !value.Definition.OneOf("Int", "Float", "ID") { + unexpectedTypeMessage(addError, value) + } + + case ast.FloatValue: + if !value.Definition.OneOf("Float") { + unexpectedTypeMessage(addError, value) + } + + case ast.StringValue, ast.BlockValue: + if value.Definition.Kind == ast.Enum { + if disableSuggestion { + addError( + Message(`Enum "%s" cannot represent non-enum value: %s.`, value.ExpectedType.String(), value.String()), + At(value.Position), + ) + } else { + rawValStr := fmt.Sprint(rawVal) + addError( + Message(`Enum "%s" cannot represent non-enum value: %s.`, value.ExpectedType.String(), value.String()), + SuggestListQuoted("Did you mean the enum value", rawValStr, possibleEnums), + At(value.Position), + ) + } + } else if !value.Definition.OneOf("String", "ID") { + unexpectedTypeMessage(addError, value) + } + + case ast.EnumValue: + if value.Definition.Kind != ast.Enum { + if disableSuggestion { + addError( + unexpectedTypeMessageOnly(value), + At(value.Position), + ) + } else { + rawValStr := fmt.Sprint(rawVal) + addError( + unexpectedTypeMessageOnly(value), + SuggestListUnquoted("Did you mean the enum value", rawValStr, possibleEnums), + At(value.Position), + ) + } + } else if value.Definition.EnumValues.ForName(value.Raw) == nil { + if disableSuggestion { + addError( + Message(`Value "%s" does not exist in "%s" enum.`, value.String(), value.ExpectedType.String()), + At(value.Position), + ) + } else { + rawValStr := fmt.Sprint(rawVal) + addError( + Message(`Value "%s" does not exist in "%s" enum.`, value.String(), value.ExpectedType.String()), + SuggestListQuoted("Did you mean the enum value", rawValStr, possibleEnums), + At(value.Position), + ) + } + } + + case ast.BooleanValue: + if !value.Definition.OneOf("Boolean") { + unexpectedTypeMessage(addError, value) + } + + case ast.ObjectValue: + + for _, field := range value.Definition.Fields { + if field.Type.NonNull { + fieldValue := value.Children.ForName(field.Name) + if fieldValue == nil && field.DefaultValue == nil { + addError( + Message(`Field "%s.%s" of required type "%s" was not provided.`, value.Definition.Name, field.Name, field.Type.String()), + At(value.Position), + ) + continue + } + } + } + + for _, directive := range value.Definition.Directives { + if directive.Name == "oneOf" { + func() { + if len(value.Children) != 1 { + addError( + Message(`OneOf Input Object "%s" must specify exactly one key.`, value.Definition.Name), + At(value.Position), + ) + return + } + + fieldValue := value.Children[0].Value + isNullLiteral := fieldValue == nil || fieldValue.Kind == ast.NullValue + if isNullLiteral { + addError( + Message(`Field "%s.%s" must be non-null.`, value.Definition.Name, value.Definition.Fields[0].Name), + At(fieldValue.Position), + ) + return + } + + isVariable := fieldValue.Kind == ast.Variable + if isVariable { + variableName := fieldValue.VariableDefinition.Variable + isNullableVariable := !fieldValue.VariableDefinition.Type.NonNull + if isNullableVariable { + addError( + Message(`Variable "%s" must be non-nullable to be used for OneOf Input Object "%s".`, variableName, value.Definition.Name), + At(fieldValue.Position), + ) + } + } + }() + } + } + + for _, fieldValue := range value.Children { + if value.Definition.Fields.ForName(fieldValue.Name) == nil { + if disableSuggestion { + addError( + Message(`Field "%s" is not defined by type "%s".`, fieldValue.Name, value.Definition.Name), + At(fieldValue.Position), + ) + } else { + var suggestions []string + for _, fieldValue := range value.Definition.Fields { + suggestions = append(suggestions, fieldValue.Name) + } + + addError( + Message(`Field "%s" is not defined by type "%s".`, fieldValue.Name, value.Definition.Name), + SuggestListQuoted("Did you mean", fieldValue.Name, suggestions), + At(fieldValue.Position), + ) + } + } + } + + case ast.Variable: + return + + default: + panic(fmt.Errorf("unhandled %T", value)) + } + }) +} + +var ValuesOfCorrectTypeRule = Rule{ + Name: "ValuesOfCorrectType", + RuleFunc: func(observers *Events, addError AddErrFunc) { + ruleFuncValuesOfCorrectType(observers, addError, false) + }, +} + +var ValuesOfCorrectTypeRuleWithoutSuggestions = Rule{ + Name: "ValuesOfCorrectTypeWithoutSuggestions", + RuleFunc: func(observers *Events, addError AddErrFunc) { + ruleFuncValuesOfCorrectType(observers, addError, true) + }, +} + +func init() { + AddRule(ValuesOfCorrectTypeRule.Name, ValuesOfCorrectTypeRule.RuleFunc) +} + +func unexpectedTypeMessage(addError AddErrFunc, v *ast.Value) { + addError( + unexpectedTypeMessageOnly(v), + At(v.Position), + ) +} + +func unexpectedTypeMessageOnly(v *ast.Value) ErrorOption { + switch v.ExpectedType.String() { + case "Int", "Int!": + if _, err := strconv.ParseInt(v.Raw, 10, 32); err != nil && errors.Is(err, strconv.ErrRange) { + return Message(`Int cannot represent non 32-bit signed integer value: %s`, v.String()) + } + return Message(`Int cannot represent non-integer value: %s`, v.String()) + case "String", "String!", "[String]": + return Message(`String cannot represent a non string value: %s`, v.String()) + case "Boolean", "Boolean!": + return Message(`Boolean cannot represent a non boolean value: %s`, v.String()) + case "Float", "Float!": + return Message(`Float cannot represent non numeric value: %s`, v.String()) + case "ID", "ID!": + return Message(`ID cannot represent a non-string and non-integer value: %s`, v.String()) + // case "Enum": + // return Message(`Enum "%s" cannot represent non-enum value: %s`, v.ExpectedType.String(), v.String()) + default: + if v.Definition.Kind == ast.Enum { + return Message(`Enum "%s" cannot represent non-enum value: %s.`, v.ExpectedType.String(), v.String()) + } + return Message(`Expected value of type "%s", found %s.`, v.ExpectedType.String(), v.String()) + } +} diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/rules/variables_are_input_types.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/variables_are_input_types.go new file mode 100644 index 0000000000..e1bf2b1f57 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/variables_are_input_types.go @@ -0,0 +1,35 @@ +package rules + +import ( + "github.com/vektah/gqlparser/v2/ast" + + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" +) + +var VariablesAreInputTypesRule = Rule{ + Name: "VariablesAreInputTypes", + RuleFunc: func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + for _, def := range operation.VariableDefinitions { + if def.Definition == nil { + continue + } + if !def.Definition.IsInputType() { + addError( + Message( + `Variable "$%s" cannot be non-input type "%s".`, + def.Variable, + def.Type.String(), + ), + At(def.Position), + ) + } + } + }) + }, +} + +func init() { + AddRule(VariablesAreInputTypesRule.Name, VariablesAreInputTypesRule.RuleFunc) +} diff --git a/internal/gqlparser/validator/rules/variables_in_allowed_position.go b/vendor/github.com/vektah/gqlparser/v2/validator/rules/variables_in_allowed_position.go similarity index 67% rename from internal/gqlparser/validator/rules/variables_in_allowed_position.go rename to vendor/github.com/vektah/gqlparser/v2/validator/rules/variables_in_allowed_position.go index 08a8e18c09..f05ee687ad 100644 --- a/internal/gqlparser/validator/rules/variables_in_allowed_position.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/rules/variables_in_allowed_position.go @@ -1,14 +1,15 @@ -package validator +package rules import ( - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" - //nolint:revive // Validator rules each use dot imports for convenience. - . "github.com/open-policy-agent/opa/internal/gqlparser/validator" + //nolint:staticcheck // Validator rules each use dot imports for convenience. + . "github.com/vektah/gqlparser/v2/validator" ) -func init() { - AddRule("VariablesInAllowedPosition", func(observers *Events, addError AddErrFunc) { +var VariablesInAllowedPositionRule = Rule{ + Name: "VariablesInAllowedPosition", + RuleFunc: func(observers *Events, addError AddErrFunc) { observers.OnValue(func(walker *Walker, value *ast.Value) { if value.Kind != ast.Variable || value.ExpectedType == nil || value.VariableDefinition == nil || walker.CurrentOperation == nil { return @@ -36,5 +37,9 @@ func init() { ) } }) - }) + }, +} + +func init() { + AddRule(VariablesInAllowedPositionRule.Name, VariablesInAllowedPositionRule.RuleFunc) } diff --git a/internal/gqlparser/validator/schema.go b/vendor/github.com/vektah/gqlparser/v2/validator/schema.go similarity index 86% rename from internal/gqlparser/validator/schema.go rename to vendor/github.com/vektah/gqlparser/v2/validator/schema.go index c9c542195d..a8754afc2b 100644 --- a/internal/gqlparser/validator/schema.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/schema.go @@ -5,21 +5,20 @@ import ( "strconv" "strings" - //nolint:revive - . "github.com/open-policy-agent/opa/internal/gqlparser/ast" - "github.com/open-policy-agent/opa/internal/gqlparser/gqlerror" - "github.com/open-policy-agent/opa/internal/gqlparser/parser" + . "github.com/vektah/gqlparser/v2/ast" //nolint:staticcheck // bad, yeah + "github.com/vektah/gqlparser/v2/gqlerror" + "github.com/vektah/gqlparser/v2/parser" ) func LoadSchema(inputs ...*Source) (*Schema, error) { - ast, err := parser.ParseSchemas(inputs...) + sd, err := parser.ParseSchemas(inputs...) if err != nil { - return nil, err + return nil, gqlerror.WrapIfUnwrapped(err) } - return ValidateSchemaDocument(ast) + return ValidateSchemaDocument(sd) } -func ValidateSchemaDocument(ast *SchemaDocument) (*Schema, error) { +func ValidateSchemaDocument(sd *SchemaDocument) (*Schema, error) { schema := Schema{ Types: map[string]*Definition{}, Directives: map[string]*DirectiveDefinition{}, @@ -27,16 +26,16 @@ func ValidateSchemaDocument(ast *SchemaDocument) (*Schema, error) { Implements: map[string][]*Definition{}, } - for i, def := range ast.Definitions { + for i, def := range sd.Definitions { if schema.Types[def.Name] != nil { return nil, gqlerror.ErrorPosf(def.Position, "Cannot redeclare type %s.", def.Name) } - schema.Types[def.Name] = ast.Definitions[i] + schema.Types[def.Name] = sd.Definitions[i] } - defs := append(DefinitionList{}, ast.Definitions...) + defs := append(DefinitionList{}, sd.Definitions...) - for _, ext := range ast.Extensions { + for _, ext := range sd.Extensions { def := schema.Types[ext.Name] if def == nil { schema.Types[ext.Name] = &Definition{ @@ -80,13 +79,13 @@ func ValidateSchemaDocument(ast *SchemaDocument) (*Schema, error) { } } - for i, dir := range ast.Directives { + for i, dir := range sd.Directives { if schema.Directives[dir.Name] != nil { // While the spec says SDL must not (§3.5) explicitly define builtin // scalars, it may (§3.13) define builtin directives. Here we check for // that, and reject doubly-defined directives otherwise. switch dir.Name { - case "include", "skip", "deprecated", "specifiedBy": // the builtins + case "include", "skip", "deprecated", "specifiedBy", "defer", "oneOf": // the builtins // In principle here we might want to validate that the // directives are the same. But they might not be, if the // server has an older spec than we do. (Plus, validating this @@ -99,16 +98,16 @@ func ValidateSchemaDocument(ast *SchemaDocument) (*Schema, error) { return nil, gqlerror.ErrorPosf(dir.Position, "Cannot redeclare directive %s.", dir.Name) } } - schema.Directives[dir.Name] = ast.Directives[i] + schema.Directives[dir.Name] = sd.Directives[i] } - if len(ast.Schema) > 1 { - return nil, gqlerror.ErrorPosf(ast.Schema[1].Position, "Cannot have multiple schema entry points, consider schema extensions instead.") + if len(sd.Schema) > 1 { + return nil, gqlerror.ErrorPosf(sd.Schema[1].Position, "Cannot have multiple schema entry points, consider schema extensions instead.") } - if len(ast.Schema) == 1 { - schema.Description = ast.Schema[0].Description - for _, entrypoint := range ast.Schema[0].OperationTypes { + if len(sd.Schema) == 1 { + schema.Description = sd.Schema[0].Description + for _, entrypoint := range sd.Schema[0].OperationTypes { def := schema.Types[entrypoint.Type] if def == nil { return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type) @@ -122,9 +121,13 @@ func ValidateSchemaDocument(ast *SchemaDocument) (*Schema, error) { schema.Subscription = def } } + if err := validateDirectives(&schema, sd.Schema[0].Directives, LocationSchema, nil); err != nil { + return nil, err + } + schema.SchemaDirectives = append(schema.SchemaDirectives, sd.Schema[0].Directives...) } - for _, ext := range ast.SchemaExtension { + for _, ext := range sd.SchemaExtension { for _, entrypoint := range ext.OperationTypes { def := schema.Types[entrypoint.Type] if def == nil { @@ -139,6 +142,10 @@ func ValidateSchemaDocument(ast *SchemaDocument) (*Schema, error) { schema.Subscription = def } } + if err := validateDirectives(&schema, ext.Directives, LocationSchema, nil); err != nil { + return nil, err + } + schema.SchemaDirectives = append(schema.SchemaDirectives, ext.Directives...) } if err := validateTypeDefinitions(&schema); err != nil { @@ -152,7 +159,7 @@ func ValidateSchemaDocument(ast *SchemaDocument) (*Schema, error) { // Inferred root operation type names should be performed only when a `schema` directive is // **not** provided, when it is, `Mutation` and `Subscription` becomes valid types and are not // assigned as a root operation on the schema. - if len(ast.Schema) == 0 { + if len(sd.Schema) == 0 { if schema.Query == nil && schema.Types["Query"] != nil { schema.Query = schema.Types["Query"] } @@ -284,6 +291,9 @@ func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error { return gqlerror.ErrorPosf(def.Position, "%s %s: non-enum value %s.", def.Kind, def.Name, value.Name) } } + if err := validateDirectives(schema, value.Directives, LocationEnumValue, nil); err != nil { + return err + } } case InputObject: if len(def.Fields) == 0 { @@ -359,11 +369,12 @@ func validateDirectives(schema *Schema, dirs DirectiveList, location DirectiveLo if currentDirective != nil && dir.Name == currentDirective.Name { return gqlerror.ErrorPosf(dir.Position, "Directive %s cannot refer to itself.", currentDirective.Name) } - if schema.Directives[dir.Name] == nil { + dirDefinition := schema.Directives[dir.Name] + if dirDefinition == nil { return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name) } validKind := false - for _, dirLocation := range schema.Directives[dir.Name].Locations { + for _, dirLocation := range dirDefinition.Locations { if dirLocation == location { validKind = true break @@ -372,6 +383,18 @@ func validateDirectives(schema *Schema, dirs DirectiveList, location DirectiveLo if !validKind { return gqlerror.ErrorPosf(dir.Position, "Directive %s is not applicable on %s.", dir.Name, location) } + for _, arg := range dir.Arguments { + if dirDefinition.Arguments.ForName(arg.Name) == nil { + return gqlerror.ErrorPosf(arg.Position, "Undefined argument %s for directive %s.", arg.Name, dir.Name) + } + } + for _, schemaArg := range dirDefinition.Arguments { + if schemaArg.Type.NonNull && schemaArg.DefaultValue == nil { + if arg := dir.Arguments.ForName(schemaArg.Name); arg == nil || arg.Value.Kind == NullValue { + return gqlerror.ErrorPosf(dir.Position, "Argument %s for directive %s cannot be null.", schemaArg.Name, dir.Name) + } + } + } dir.Definition = schema.Directives[dir.Name] } return nil @@ -379,7 +402,7 @@ func validateDirectives(schema *Schema, dirs DirectiveList, location DirectiveLo func validateImplements(schema *Schema, def *Definition, intfName string) *gqlerror.Error { // see validation rules at the bottom of - // https://facebook.github.io/graphql/October2021/#sec-Objects + // https://spec.graphql.org/October2021/#sec-Objects intf := schema.Types[intfName] if intf == nil { return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intfName)) diff --git a/internal/gqlparser/validator/schema_test.yml b/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml similarity index 92% rename from internal/gqlparser/validator/schema_test.yml rename to vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml index 7034a4697c..22f125bec4 100644 --- a/internal/gqlparser/validator/schema_test.yml +++ b/vendor/github.com/vektah/gqlparser/v2/validator/schema_test.yml @@ -80,6 +80,15 @@ object types: message: 'Name "__id" must not begin with "__", which is reserved by GraphQL introspection.' locations: [{line: 2, column: 3}] + - name: field argument list must not be empty + input: | + type FooBar { + foo(): ID + } + error: + message: 'expected at least one definition, found )' + locations: [{line: 2, column: 7}] + - name: check reserved names on type field argument input: | type FooBar { @@ -528,7 +537,16 @@ directives: directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - - name: must be declared + - name: must be declared (type) + input: | + type User @foo { + name: String + } + error: + message: "Undefined directive foo." + locations: [{line: 1, column: 12}] + + - name: must be declared (field) input: | type User { name: String @foo @@ -537,6 +555,15 @@ directives: message: "Undefined directive foo." locations: [{line: 2, column: 17}] + - name: must be declared (enum) + input: | + enum Unit { + METER @foo + } + error: + message: "Undefined directive foo." + locations: [{line: 2, column: 10}] + - name: cannot be self-referential input: | directive @A(foo: Int! @A) on FIELD_DEFINITION @@ -604,6 +631,32 @@ directives: type P { name: String @testField } interface I { id: ID @testField } + - name: Invalid directive argument not allowed + input: | + directive @foo(bla: Int!) on FIELD_DEFINITION + type P {f: Int @foo(foobla: 11)} + + error: + message: 'Undefined argument foobla for directive foo.' + locations: [{line: 2, column: 21}] + + - name: non-null argument must be provided + input: | + directive @foo(bla: Int!) on FIELD_DEFINITION + type P {f: Int @foo } + + error: + message: 'Argument bla for directive foo cannot be null.' + locations: [{line: 2, column: 17}] + + - name: non-null argument must not be null + input: | + directive @foo(bla: Int!) on FIELD_DEFINITION + type P {f: Int @foo(bla: null) } + + error: + message: 'Argument bla for directive foo cannot be null.' + locations: [{line: 2, column: 17}] entry points: - name: multiple schema entry points diff --git a/internal/gqlparser/validator/suggestionList.go b/vendor/github.com/vektah/gqlparser/v2/validator/suggestionList.go similarity index 100% rename from internal/gqlparser/validator/suggestionList.go rename to vendor/github.com/vektah/gqlparser/v2/validator/suggestionList.go diff --git a/vendor/github.com/vektah/gqlparser/v2/validator/validator.go b/vendor/github.com/vektah/gqlparser/v2/validator/validator.go new file mode 100644 index 0000000000..1b4040c2c8 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/v2/validator/validator.go @@ -0,0 +1,93 @@ +package validator + +import ( + //nolint:staticcheck // bad, yeah + . "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/gqlerror" +) + +type AddErrFunc func(options ...ErrorOption) + +type RuleFunc func(observers *Events, addError AddErrFunc) + +type Rule struct { + Name string + RuleFunc RuleFunc +} + +var specifiedRules []Rule + +// AddRule adds a rule to the rule set. +// ruleFunc is called once each time `Validate` is executed. +func AddRule(name string, ruleFunc RuleFunc) { + specifiedRules = append(specifiedRules, Rule{Name: name, RuleFunc: ruleFunc}) +} + +// RemoveRule removes an existing rule from the rule set +// if one of the same name exists. +// The rule set is global, so it is not safe for concurrent changes +func RemoveRule(name string) { + var result []Rule // nolint:prealloc // using initialized with len(rules) produces a race condition + for _, r := range specifiedRules { + if r.Name == name { + continue + } + result = append(result, r) + } + specifiedRules = result +} + +// ReplaceRule replaces an existing rule from the rule set +// if one of the same name exists. +// If no match is found, it will add a new rule to the rule set. +// The rule set is global, so it is not safe for concurrent changes +func ReplaceRule(name string, ruleFunc RuleFunc) { + var found bool + var result []Rule // nolint:prealloc // using initialized with len(rules) produces a race condition + for _, r := range specifiedRules { + if r.Name == name { + found = true + result = append(result, Rule{Name: name, RuleFunc: ruleFunc}) + continue + } + result = append(result, r) + } + if !found { + specifiedRules = append(specifiedRules, Rule{Name: name, RuleFunc: ruleFunc}) + return + } + specifiedRules = result +} + +func Validate(schema *Schema, doc *QueryDocument, rules ...Rule) gqlerror.List { + if rules == nil { + rules = specifiedRules + } + + var errs gqlerror.List + if schema == nil { + errs = append(errs, gqlerror.Errorf("cannot validate as Schema is nil")) + } + if doc == nil { + errs = append(errs, gqlerror.Errorf("cannot validate as QueryDocument is nil")) + } + if len(errs) > 0 { + return errs + } + observers := &Events{} + for i := range rules { + rule := rules[i] + rule.RuleFunc(observers, func(options ...ErrorOption) { + err := &gqlerror.Error{ + Rule: rule.Name, + } + for _, o := range options { + o(err) + } + errs = append(errs, err) + }) + } + + Walk(schema, doc, observers) + return errs +} diff --git a/internal/gqlparser/validator/vars.go b/vendor/github.com/vektah/gqlparser/v2/validator/vars.go similarity index 94% rename from internal/gqlparser/validator/vars.go rename to vendor/github.com/vektah/gqlparser/v2/validator/vars.go index 66924148ba..205a7fb516 100644 --- a/internal/gqlparser/validator/vars.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/vars.go @@ -2,17 +2,17 @@ package validator import ( "encoding/json" - "errors" "fmt" "reflect" "strconv" "strings" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" - "github.com/open-policy-agent/opa/internal/gqlparser/gqlerror" + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/gqlerror" ) -var ErrUnexpectedType = errors.New("Unexpected Type") +//nolint:staticcheck // We do not care about capitalized error strings +var ErrUnexpectedType = fmt.Errorf("Unexpected Type") // VariableValues coerces and validates variable values func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables map[string]interface{}) (map[string]interface{}, error) { @@ -56,19 +56,19 @@ func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables m jsonNumber, isJSONNumber := val.(json.Number) if isJSONNumber { - if v.Type.NamedType == "Int" { + switch v.Type.NamedType { + case "Int": n, err := jsonNumber.Int64() if err != nil { return nil, gqlerror.ErrorPathf(validator.path, "cannot use value %d as %s", n, v.Type.NamedType) } rv = reflect.ValueOf(n) - } else if v.Type.NamedType == "Float" { + case "Float": f, err := jsonNumber.Float64() if err != nil { return nil, gqlerror.ErrorPathf(validator.path, "cannot use value %f as %s", f, v.Type.NamedType) } rv = reflect.ValueOf(f) - } } if rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface { @@ -107,7 +107,7 @@ func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflec slc = reflect.Append(slc, val) val = slc } - for i := range val.Len() { + for i := 0; i < val.Len(); i++ { resetPath() v.path = append(v.path, ast.PathIndex(i)) field := val.Index(i) @@ -182,7 +182,7 @@ func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflec return val, gqlerror.ErrorPathf(v.path, "cannot use %s as %s", kind.String(), typ.NamedType) case ast.InputObject: if val.Kind() != reflect.Map { - return val, gqlerror.ErrorPathf(v.path, "must be a %s", def.Name) + return val, gqlerror.ErrorPathf(v.path, "must be a %s, not a %s", def.Name, val.Kind()) } // check for unknown fields diff --git a/internal/gqlparser/validator/walk.go b/vendor/github.com/vektah/gqlparser/v2/validator/walk.go similarity index 98% rename from internal/gqlparser/validator/walk.go rename to vendor/github.com/vektah/gqlparser/v2/validator/walk.go index f722871869..d3140746fb 100644 --- a/internal/gqlparser/validator/walk.go +++ b/vendor/github.com/vektah/gqlparser/v2/validator/walk.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/open-policy-agent/opa/internal/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" ) type Events struct { @@ -22,27 +22,35 @@ type Events struct { func (o *Events) OnOperation(f func(walker *Walker, operation *ast.OperationDefinition)) { o.operationVisitor = append(o.operationVisitor, f) } + func (o *Events) OnField(f func(walker *Walker, field *ast.Field)) { o.field = append(o.field, f) } + func (o *Events) OnFragment(f func(walker *Walker, fragment *ast.FragmentDefinition)) { o.fragment = append(o.fragment, f) } + func (o *Events) OnInlineFragment(f func(walker *Walker, inlineFragment *ast.InlineFragment)) { o.inlineFragment = append(o.inlineFragment, f) } + func (o *Events) OnFragmentSpread(f func(walker *Walker, fragmentSpread *ast.FragmentSpread)) { o.fragmentSpread = append(o.fragmentSpread, f) } + func (o *Events) OnDirective(f func(walker *Walker, directive *ast.Directive)) { o.directive = append(o.directive, f) } + func (o *Events) OnDirectiveList(f func(walker *Walker, directives []*ast.Directive)) { o.directiveList = append(o.directiveList, f) } + func (o *Events) OnValue(f func(walker *Walker, value *ast.Value)) { o.value = append(o.value, f) } + func (o *Events) OnVariable(f func(walker *Walker, variable *ast.VariableDefinition)) { o.variable = append(o.variable, f) } @@ -277,7 +285,7 @@ func (w *Walker) walkSelection(parentDef *ast.Definition, it ast.Selection) { w.walkDirectives(nextParentDef, it.Directives, ast.LocationFragmentSpread) if def != nil && !w.validatedFragmentSpreads[def.Name] { - // prevent inifinite recursion + // prevent infinite recursion w.validatedFragmentSpreads[def.Name] = true w.walkSelectionSet(nextParentDef, def.SelectionSet) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 89c2f27fe8..46fbc05c80 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -271,6 +271,14 @@ github.com/subosito/gotenv # github.com/tchap/go-patricia/v2 v2.3.2 ## explicit; go 1.16 github.com/tchap/go-patricia/v2/patricia +# github.com/vektah/gqlparser/v2 v2.5.26 +## explicit; go 1.22 +github.com/vektah/gqlparser/v2/ast +github.com/vektah/gqlparser/v2/gqlerror +github.com/vektah/gqlparser/v2/lexer +github.com/vektah/gqlparser/v2/parser +github.com/vektah/gqlparser/v2/validator +github.com/vektah/gqlparser/v2/validator/rules # github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb ## explicit github.com/xeipuuv/gojsonpointer